Compare commits

..

724 Commits

Author SHA1 Message Date
Max Kellermann
fff25ac753 release v0.23.1 2021-10-19 10:27:28 +02:00
Max Kellermann
4f1e79b6b8 filter/ReplayGain: emit "mixer" event when replay gain changes volume
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1294
2021-10-19 10:03:21 +02:00
Max Kellermann
aa9933c0b5 output/pipewire: add noexcept 2021-10-19 08:58:50 +02:00
Max Kellermann
0697d1f859 output/pipewire: include cleanup 2021-10-19 08:57:33 +02:00
Max Kellermann
df033fa4aa NEWS: mention the previous commit 2021-10-19 08:56:32 +02:00
Nicolai Syvertsen
b941a7df83 Implement volume updates for pipewire output 2021-10-19 00:01:45 +02:00
Max Kellermann
31151cec3c command/playlist: "load" supports relative positions
This commit also increases the PROTOCOL_VERSION so clients can detect
the availability of the feature.
2021-10-18 22:08:22 +02:00
Max Kellermann
07e8c338df command/queue: move position parameter functions to separate library 2021-10-18 22:07:04 +02:00
Max Kellermann
b22d7218aa command/player, ...: use decimal notation
During the libfmt migration, I converted "%1.3f" to just "{:1.3}"
without the "f" suffix, but libfmt defaults to scientific notation,
which can break some MPD clients.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1291
2021-10-18 16:54:53 +02:00
Max Kellermann
d5be8c74b0 output/pipewire: attempt to change the graph sample rate
Requires PipeWire 0.3.32.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1283
2021-10-18 16:46:23 +02:00
Max Kellermann
c112cb60da output/snapcast: fix typo which caused "Failed to get chunk"
This bug caused a 9 second offset in all time stamps.  Due to that,
the Snapcast server thought the chunks are too old and discarded them.

Fixes https://github.com/MusicPlayerDaemon/MPD/discussions/1287
2021-10-18 16:40:11 +02:00
Max Kellermann
677fa4f9bc doc/plugins.rst: mention that the snapcast output requires a format 2021-10-17 20:01:21 +02:00
Max Kellermann
907af2ad02 Permission: refactor getPermissionFromPassword() to return std::optional
This replaces the output parameter (which is bad API design).  As a
side effect, it fixes the bad [[gnu::pure]] attribute added by commit
a636d2127 which caused optimizing compilers to miscompile calls to
that function.  "Pure" functions can be assumed to have no output
arguments, so the compiler can assume the function doesn't modify
them.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1282
2021-10-17 19:58:50 +02:00
Thomas Zander
6a2e7bbc02 protocol/ArgParser.cxx: Add missing #include <stdio.h>
Fixes a build problem on platforms where stdio.h is not included
transitively. snprintf() is defined in stdio.h.
2021-10-16 17:38:07 +02:00
Max Kellermann
771c46032f meson.build: add missing libfmt dependencies
Fixes https://github.com/MusicPlayerDaemon/MPD/discussions/1281

The problem occurred when there was libfmt-dev installed, but it was
too old (e.g. on Debian Buster), and Meson used the wrap fallback.
Those internal MPD libraries where the libfmt dependency was not
declared were still using the old system libfmt headers, which are not
ABI-compatible with MPD's own libfmt build.
2021-10-15 14:26:59 +02:00
Max Kellermann
85611aa456 storage/smbclient: add StoragePlugin.prefixes
Should have been part of commit
ef24cfa523

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1279
2021-10-15 10:24:30 +02:00
Max Kellermann
466b5cb08d neighbor/smbclient: FmtError() instead of FormatErrno()
Fixes part 2 of https://github.com/MusicPlayerDaemon/MPD/issues/1279
2021-10-15 09:40:36 +02:00
Max Kellermann
3f2f3251cb neighbor/smbclient: use [[gnu::pure]]
Fixes part 1 of https://github.com/MusicPlayerDaemon/MPD/issues/1279
2021-10-15 09:39:34 +02:00
Max Kellermann
8ae85f3991 doc/protocol.rst: move POSITION from "search" to "findadd"
Whoops, I misplaced this one.
2021-10-14 15:36:25 +02:00
Max Kellermann
781fe4ff28 increment version number to 0.23.1 2021-10-14 15:36:16 +02:00
Max Kellermann
163c59128e release v0.23 2021-10-14 15:25:21 +02:00
Max Kellermann
608896571c command/queue: add position parameter to "load"
Another one from https://github.com/MusicPlayerDaemon/MPD/issues/888
2021-10-14 15:11:11 +02:00
Max Kellermann
2e5ca1cbd2 command/database: add "position" parameter to "findadd" and "searchadd"
Closes https://github.com/MusicPlayerDaemon/MPD/issues/888
2021-10-14 15:03:02 +02:00
Max Kellermann
680fb51c37 Permission: add "player" to default permission set
Forgot that in commit 9a766f4cd9
2021-10-14 14:58:38 +02:00
Max Kellermann
77d74b404e Permission: add option "host_permissions"
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1115
2021-10-14 14:44:18 +02:00
Max Kellermann
a636d2127a Permission: add "pure" attributes 2021-10-14 14:26:51 +02:00
Max Kellermann
9a766f4cd9 Permission: split permission "player" from "control"
Some users want certain clients to fully control playback, but do not
want them to be able to trigger database update.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1124
2021-10-14 14:19:51 +02:00
Max Kellermann
8ad17d25ef player/CrossFade: do not cross-fade songs shorter than 20 seconds
From the feature request: "I generally like to have crossfade on, but
when it happens during such short tracks (e.g. 20 seconds or less) it
doesn't really sound good as those tracks are not really meant to be
crossfaded and intended to act as a bridge on their own."

Sounds reasonable.  This commit doesn't add an option, but hard-codes
the limit to 20 seconds.  If it turns out that users want to have it
configurable, we can still add the option.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1184
2021-10-14 13:47:24 +02:00
Max Kellermann
46d00dd85f player/CrossFade: move code to CanCrossFadeSong() 2021-10-14 13:42:27 +02:00
Max Kellermann
ec52b13449 player/CrossFade: add method IsEnabled() 2021-10-14 13:41:50 +02:00
Max Kellermann
cf6ca1b0ba player/CrossFade: use C++11 initializers 2021-10-14 13:38:42 +02:00
Max Kellermann
37bd6de658 db/simple: add option to hide CUE target songs
This reduces duplicates in the music database by hiding the original
song file when it is referenced by a CUE sheet.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1275
2021-10-14 13:28:37 +02:00
Max Kellermann
f7622ca332 db/update/Walk: move PurgeDanglingFromPlaylists() to Playlist.cxx 2021-10-14 13:12:10 +02:00
Max Kellermann
b82b56970b db/simple/Song: reorder fields for better packing 2021-10-14 12:55:02 +02:00
Max Kellermann
e4eb5b79c9 output/shout: move shout_new() call to Enable()
Don't allocate any memory until the output is really enabled.
2021-10-14 12:28:36 +02:00
Max Kellermann
1cfea20b22 output/shout: remove the defunct "timeout" option
The implementation was removed 12 years ago in commit f6455d5f79 and
nobody missed it.
2021-10-14 12:04:13 +02:00
Max Kellermann
efa3ffa8d8 Revert "db/update/playlist: remove non-existent targets while scanning"
This reverts commit 9200fa6d06.  It was
wrong because it works only if the target song has already been
scanned.
2021-10-14 11:50:41 +02:00
Max Kellermann
1b8c94d6b9 db/update/Playlist: move code to another UpdatePlaylistFile() method 2021-10-14 11:36:45 +02:00
Max Kellermann
cd5c1f3f45 db/update/playlist: remove empty playlist directories 2021-10-13 19:23:24 +02:00
Max Kellermann
9200fa6d06 db/update/playlist: remove non-existent targets while scanning 2021-10-13 19:23:24 +02:00
Max Kellermann
1bbe9896f6 Main: make inotify errors non-fatal 2021-10-13 18:55:05 +02:00
Max Kellermann
2d8847f428 db/update/InotifyUpdate: convert to class, no global variables 2021-10-13 18:47:56 +02:00
Max Kellermann
72f6e018e7 Log: remove the obsolete printf-style functions 2021-10-13 17:41:19 +02:00
Max Kellermann
2fbbd540bb more [[gnu::...]] attributes 2021-10-13 17:38:01 +02:00
Max Kellermann
18f64b5fb7 system/FatalError: remove obsolete library 2021-10-13 16:53:01 +02:00
Max Kellermann
d2a8b1e8a5 db/update/InotifySource: make errors non-fatal 2021-10-13 16:53:01 +02:00
Max Kellermann
184e8eca7c win32/Win32Main: throw on error 2021-10-13 16:37:56 +02:00
Max Kellermann
0a8886704a Main: move top-level exception handler to main()
Allows win32_main() to throw exceptions.
2021-10-13 16:37:34 +02:00
Max Kellermann
0712314d23 archive/{zzip,iso9660}: ignore file names which are invalid UTF-8
These malformed strings must not be transferred over the wire, because
the MPD protocol is defined to be UTF-8.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1274
2021-10-13 15:51:08 +02:00
Max Kellermann
f8cbba1850 util/Alloc: remove unused library 2021-10-13 14:48:16 +02:00
Max Kellermann
635ec3ce37 util/VarSize: use plain malloc() 2021-10-13 14:46:40 +02:00
Max Kellermann
8e71130e8a tag/FixString: use class AllocatedArray 2021-10-13 14:40:33 +02:00
Max Kellermann
ed7baf3ae1 tag/Id3Scan: use StringView::Strip() instead of duplicating the string 2021-10-13 14:32:43 +02:00
Max Kellermann
1e159af2ce tag/Id3Scan: merge duplicate code into InvokeOnTag() 2021-10-13 14:30:57 +02:00
Max Kellermann
dffed6e393 tag/Id3Scan: add class Id3String 2021-10-13 14:24:17 +02:00
Max Kellermann
bf656af555 playlist/SoundCloud: use AllocatedString for concatenation 2021-10-13 12:47:57 +02:00
Max Kellermann
d2b8852d19 playlist/SoundCloud: move code to TranslateSoundCloudUri() 2021-10-13 12:23:15 +02:00
Max Kellermann
7d4de71899 sticker/SongSticker: use AllocatedString for concatenation 2021-10-13 12:19:45 +02:00
Max Kellermann
e1c16d78e4 decoder/wavpack: use AllocatedString for concatenation 2021-10-13 12:15:57 +02:00
Max Kellermann
a49b49cba7 decoder/wavpack: fix WVC file support
The wrong variable was used.  This has been broken by commit
4eeea640f4 seven years ago - and nobody as noticed, d'oh!
2021-10-13 12:11:39 +02:00
Max Kellermann
f510564d9d more [[gnu::...]] attributes 2021-10-13 12:07:05 +02:00
Max Kellermann
1c4b484a56 avahi/Poll: use FineTimerEvent in AvahiTimeout
libavahi-client uses this one to schedule events immediately.
However, CoarseTimerEvent may be called too late, and cause timeouts.
2021-10-13 11:13:54 +02:00
Max Kellermann
b394d8d059 net/Resolver: include cleanup 2021-10-13 11:06:10 +02:00
Max Kellermann
a15c1c71d5 util/WritableBuffer: add WritableBuffer<void>::{FromVoid,ToVoid}() 2021-10-13 11:05:33 +02:00
Max Kellermann
8d679e7e00 util/IntrusiveList: add IntrusiveList::swap() 2021-10-13 11:04:42 +02:00
Max Kellermann
2b30ac2351 util/IntrusiveList: add another missing ToNode() call 2021-10-13 11:04:21 +02:00
Max Kellermann
1c97793b49 util/IntrusiveList: do not use the deprecated class std::iterator
Deprecated in C++17.  Since C++17, one is supposed to declare those 5
types manually.
2021-10-13 11:03:49 +02:00
Max Kellermann
4dae8b41da event/PipeEvent: new class wrapping SocketEvent 2021-10-13 10:45:55 +02:00
Max Kellermann
be8ed2f59e tag/Settings: use [[gnu::const]] 2021-10-13 10:37:26 +02:00
Max Kellermann
08491fcd86 tag/Format: use [[gnu::pure]] 2021-10-13 10:37:26 +02:00
Max Kellermann
abed633fcb tag/FixString: use [[gnu::pure]] 2021-10-13 10:37:26 +02:00
Max Kellermann
db2a9cb6d5 tag/Builder: use [[gnu::pure]] 2021-10-13 10:37:26 +02:00
Max Kellermann
7caeb3b0d8 tag/ParseName: use [[gnu::pure]] 2021-10-13 10:32:22 +02:00
Max Kellermann
08500be239 tag/VorbisComment: use [[gnu::pure]] 2021-10-13 10:31:51 +02:00
Max Kellermann
3ef7d8fecb tag/Tag: use [[gnu::pure]] 2021-10-13 10:31:51 +02:00
Max Kellermann
ffde7223b9 tag/Table: use [[gnu::pure]] 2021-10-13 10:31:51 +02:00
Max Kellermann
4e84fa4a00 RemoteTagCache: use [[gnu::pure]] 2021-10-13 10:31:51 +02:00
Max Kellermann
78e49928b6 command/QueueCommands: disallow moving the current song relative to itself
This was a no-op previously, but this operation makes no sense.
2021-10-07 22:55:41 +02:00
Max Kellermann
c0bcfe244c command/QueueCommands: reimplement relative "move"/"moveid" offsets
The existing implementation has been utterly broken forever; I cannot
explain what it actually does, but it doesn't do what the
documentation says.
2021-10-07 22:49:38 +02:00
Max Kellermann
e63ecd81ec command/QueueCommands: eliminate id lookup from handle_addid()
Use MoveRange() instead of MoveId().
2021-10-07 22:44:06 +02:00
Max Kellermann
c47a858d15 command/QueueCommands: move code to RequireCurrentPosition() 2021-10-07 22:21:00 +02:00
Max Kellermann
076c9a0dd9 command/QueueCommands: offset relative "addid" positions by one
Now, "+0" means "right after the current song" and "-0" means "right
before the current song".  Mnemonic: there are zero songs between the
current song and the newly added song.
2021-10-07 22:12:10 +02:00
Max Kellermann
3993176b76 command/QueueCommands: support relative offsets in "addid"
A similar feature was present long ago in MPD, but was deprecated in
version 0.16 because the implementation was broken.  This commit
re-adds the feature in a way that's well-defined and not broken.

Close https://github.com/MusicPlayerDaemon/MPD/issues/1221
2021-10-07 21:55:56 +02:00
Max Kellermann
16cad48641 command/QueueCommands: validate the "addid" position before adding the song
Validate early, so we avoid the rollback if an error occurs.
2021-10-07 21:27:07 +02:00
Max Kellermann
7a6d0c2efc command/Queue: move LookupRemoteTag() to the end
Skip the LookupRemoteTag() call if the MoveId() call fails.
2021-10-07 21:13:45 +02:00
Max Kellermann
f6035f2dda util/UriRelative: use std::string_view
Eliminates lots of implicit std::string temporaries.
2021-10-07 14:49:53 +02:00
Max Kellermann
c34a1e29de util/UriRelative: fix variable mixup 2021-10-07 14:44:41 +02:00
Max Kellermann
41a69027c2 test/util/TestUriRelative: add failing test for "./" prefix 2021-10-07 14:44:30 +02:00
Max Kellermann
711c614528 python/build/libs.py: update WildMidi to 0.4.4 2021-10-07 13:34:04 +02:00
Max Kellermann
6acb240f69 python/build/libs.py: update CURL to 7.79.1 2021-10-07 13:31:00 +02:00
Max Kellermann
45f3dd8b7a Revert "python/build/libs.py: remove flac, switch to Meson wrap"
This reverts commit 6ed4aff4d3.  The
Meson wrap is broken on non-x86, because it unconditionally includes
<cpuid.h> which is an x86 only header.
2021-10-07 13:28:17 +02:00
James D. Smith
acc1bd6297 playlist/PlaylistSong: Remove dots from playlist file paths. 2021-10-07 13:15:40 +02:00
James D. Smith
49ed9dae34 util/UriUtil: New uri_squash_dot_segments. 2021-10-07 13:14:54 +02:00
Max Kellermann
cf554d306d LocateUri: implement UriPluginKind::STORAGE properly
This way, URI schemes supported by arbitrary storage plugins are
allowed.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1270
2021-10-06 20:36:39 +02:00
Max Kellermann
ef24cfa523 storage/Plugin: add "prefixes" 2021-10-06 20:14:01 +02:00
Max Kellermann
5d35983298 decoder/openmpt: catch libopenmpt exceptions in scan_stream()
Fixes crash bug.
2021-10-06 20:14:01 +02:00
Max Kellermann
2dacb36789 LocateUri: throw std::invalid_argument instead of std::runtime_error
This should translate to ACK_ERROR_ARG instead of ACK_ERROR_UNKNOWN.
2021-10-06 19:26:33 +02:00
Max Kellermann
57a1403f08 output/pipewire: implement Delay(), fix busy loop while paused
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1265
2021-09-24 11:33:35 +02:00
Max Kellermann
bad3283182 output/pipewire: add flag "active", replaces some uses of "paused"
This way, we know whether we're explicitly "paused" or "not yet
activated because the ring buffer hasn't been filled yet".
2021-09-24 11:33:30 +02:00
Max Kellermann
6ed4aff4d3 python/build/libs.py: remove flac, switch to Meson wrap 2021-09-21 16:44:51 +02:00
Max Kellermann
e525465592 python/build/verify.py: support SHA256 2021-09-21 16:22:21 +02:00
Max Kellermann
10782f4c84 {android,win32}/build.py: use makedirs(exist_ok) 2021-09-21 16:04:31 +02:00
Max Kellermann
2a02576d6d python/build/meson.py: move c_args, ... to [built-in options]
As per Meson 0.56.0:
 https://mesonbuild.com/Machine-files.html#properties
2021-09-21 14:57:55 +02:00
Max Kellermann
9ea1578a97 lib/expat/meson.build: propagate the libexpat dependency
Fixes the build with libexpat from Meson wrap.
2021-09-21 14:38:00 +02:00
Max Kellermann
520028dcfc python/build/libs.py: remove libexpat, switch to Meson wrap 2021-09-21 14:08:23 +02:00
Max Kellermann
e98cef06c7 python/build/meson.py: remove --libdir=lib MultiArch workaround
Not necessary anymore since Meson 0.50.0:
 https://mesonbuild.com/Release-notes-for-0-50-0.html#libdir-defaults-to-lib-when-cross-compiling
2021-09-21 13:58:45 +02:00
Max Kellermann
aef0535c55 python/build/libs.py: remove libvorbis, switch to Meson wrap 2021-09-21 13:49:50 +02:00
Max Kellermann
6b1d0cb01d meson.build: disable compiler warnings for subprojects
Requires Meson 0.56.0:
 https://mesonbuild.com/Release-notes-for-0-56-0.html#per-subproject-warning_level-option
2021-09-21 13:48:16 +02:00
Max Kellermann
f23ecf00da meson.build: set per-subproject "default_library"
Requires Meson 0.54.0:
 https://mesonbuild.com/Release-notes-for-0-54-0.html#per-subproject-default_library-and-werror-options
2021-09-21 13:41:45 +02:00
Max Kellermann
a1c1e26875 meson.build: require Meson 0.56.0
I want to use per-subproject warning_level (0.56.0), default_library
(0.54.0).
2021-09-21 13:39:44 +02:00
Max Kellermann
410b8711f2 lib/curl/meson.build: add -DCURL_STATICLIB on Windows
Necessary since commit 6acf81d5ae
2021-09-21 13:24:35 +02:00
Max Kellermann
6acf81d5ae python/build/libs.py: build CURL with cmake 2021-09-21 12:30:39 +02:00
Max Kellermann
4eb56d844e python/build/libs.py: update Boost to 1.77.0 2021-09-21 12:22:17 +02:00
Max Kellermann
5faf6d061f python/build/libs.py: build expat with cmake 2021-09-20 23:23:20 +02:00
Max Kellermann
d5a9f6d79d python/build/libs.py: build libogg, libvorbis with cmake 2021-09-20 23:23:20 +02:00
Max Kellermann
2699889342 python/build/cmake.py: use CMAKE_TOOLCHAIN_FILE
Let cmake know that we're cross-crompiling.
2021-09-20 23:14:13 +02:00
Max Kellermann
e4f933361e python/build/libs.py: update OpenSSL to 3.0.0 2021-09-20 22:54:02 +02:00
Max Kellermann
6f278977e9 build/python/project: add build(), calls _build()
Prepare to add some code around the _build() call.
2021-09-20 22:54:02 +02:00
Max Kellermann
4f2f705dca build/python/make: rename build() to build_make()
Eliminate conflict with other Project classes.
2021-09-20 22:54:02 +02:00
Max Kellermann
f31e38145d python/build/cmake.py: some ccache support 2021-09-20 22:54:02 +02:00
Max Kellermann
0231622169 build/python/cmake: add "windows_configure_args" 2021-09-20 22:46:54 +02:00
Max Kellermann
937423dbcf event/Loop: check the quit flag after RunDeferred()
Allow DeferredEvents to call EventLoop::Break().
2021-09-10 12:14:18 +02:00
Rosen Penev
40483d8478 fix wrong emplace usage
emplace already calls std::pair. No need for it again.

No need to emplace when calling std::make_shared.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-09-07 21:14:37 -07:00
Rosen Penev
6ec5089cc9 remove std::make_pair
make_pair is an old C++98 function that can be replaced by modern
shorter constructs.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-09-07 21:13:22 -07:00
iconoclasthero
15f419e1cb Update user.rst
NB: Check the sysconfdir setting to determine where mpd will look for mpd.conf; if you expect mpd to look for /etc/mpd.conf the sysconfdir must be '/etc' (i.e., not 'etc' which will result in mpd looking for /usr/local/etc/mpd.conf):
 
.. code-block:: none
 
 meson configure output/release |grep sysconfdir

If this is not /etc (or another path you wish to specify):

.. code-block:: none

 $ meson configure output/release -Dsysconfdir='/etc' ; meson configure output/release |grep syscon
  sysconfdir              /etc                                               Sysconf data directory
2021-09-06 12:41:41 -04:00
Max Kellermann
bdd8c34c67 Merge branch 'move' of git://github.com/neheb/MPD 2021-08-29 06:47:05 +02:00
Max Kellermann
c9a9248c9f java/Class: use Java::LocalObject
This eliminates all but one DeleteLocalRef() call.
2021-08-28 08:09:54 +02:00
Max Kellermann
31f7fede30 java/GlobalRef: remove method Set(), always require initialization 2021-08-28 08:08:30 +02:00
Max Kellermann
917fe549b0 java/Object: use type aliases 2021-08-28 08:05:57 +02:00
Max Kellermann
8e430e55af Java/GlobalRef: add LocalRef cast constructor 2021-08-28 08:04:20 +02:00
Max Kellermann
9e61bda592 java/String: add class StringUTFChars() 2021-08-27 12:30:43 +02:00
Max Kellermann
56997290d7 io/BufferedOutputStream: add method Discard() 2021-08-27 12:06:36 +02:00
Max Kellermann
d2f84f3df8 io/BufferedOutputStream: allow specifying the buffer size 2021-08-27 12:06:20 +02:00
Max Kellermann
9da28e5c73 io/BufferedOutputStream: more API documentation 2021-08-27 12:06:02 +02:00
Max Kellermann
d1f9b06f84 io/BufferedOutputStream: add WriteT() 2021-08-27 12:05:26 +02:00
Max Kellermann
f9f3306db9 io/BufferedOutputStream: use std::size_t 2021-08-27 12:05:06 +02:00
Max Kellermann
19d19cd737 fs/io/BufferedOutputStream: avoid including windows.h
Include the most specific header documented by MSDN instead.
2021-08-27 12:04:44 +02:00
Max Kellermann
b1175acb59 util/StringView: hard-code C++17 2021-08-27 12:01:31 +02:00
Max Kellermann
672278e5fd util/StringView: use [[gnu::]] attributes 2021-08-27 11:59:37 +02:00
Max Kellermann
da155f8822 util/StringCompare: use [[gnu::]] attributes 2021-08-27 11:58:25 +02:00
Max Kellermann
4026ef63b6 util/StringAPI: use [[gnu::]] attributes 2021-08-27 11:57:56 +02:00
Max Kellermann
b282682ba5 use using instead of typedef 2021-08-27 11:57:33 +02:00
Max Kellermann
ad00926e1b util/AllocatedArray: add method release() 2021-08-27 11:24:50 +02:00
Rosen Penev
0b774df375 prevent use after move
These should be equivalent anyway.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-08-26 13:38:36 -07:00
Rosen Penev
53ffcf455c make several member functions const
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-08-26 13:15:52 -07:00
Max Kellermann
9ca64d5fb3 filter/Chain: eliminate, just use a chain of TwoFilters instead
The ChainFilter class is extremely complicated code, and will grow to
be even more complicated when the Filter interface gets extended.
Let's just remove it; we can easily chain many TwoFilters instead.
2021-08-26 17:45:23 +02:00
Max Kellermann
bd79354f32 filter/TwoFilters: add ChainFilters() 2021-08-26 17:42:25 +02:00
Max Kellermann
49dcac5c21 filter/TwoFilters: add class PreparedTwoFilters 2021-08-26 17:36:19 +02:00
Max Kellermann
38a4b0d8d5 filter/TwoFilters: fix include guard 2021-08-26 14:36:52 +02:00
Max Kellermann
a224225e48 pcm/AudioFormat: use std::size_t 2021-08-26 13:42:15 +02:00
Max Kellermann
7d7fe756b3 pcm/AudioFormat: use [[gnu::pure]] 2021-08-26 13:42:15 +02:00
Max Kellermann
1cb7fe12ff pcm/AudioFormat: add noexcept 2021-08-26 13:37:36 +02:00
Max Kellermann
8a29805767 Merge tag 'v0.22.11'
release v0.22.11
2021-08-24 22:19:38 +02:00
Max Kellermann
94c196108d release v0.22.11 2021-08-24 22:15:22 +02:00
Max Kellermann
263d1ba002 Main: playlist_directory defaults to "/sdcard/Android/data/org.musicpd/files/playlists"
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1233
2021-08-24 22:12:27 +02:00
Max Kellermann
2dba06dc34 android/Context: add GetExternalFilesDir() 2021-08-24 22:03:53 +02:00
Max Kellermann
811860c3b4 android/Context: use [[gnu::pure]] 2021-08-24 21:54:22 +02:00
Max Kellermann
8439119e24 filter/ffmpeg: support double-precision samples
Insert an "aformat" filter which converts double-precision to
single-precision.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1235
2021-08-24 13:45:57 +02:00
Max Kellermann
b5b40d8235 filter/ffmpeg: automatically retry with "aformat"
If DetectFilterOutputFormat() fails to determine the output format,
insert an "aformat" filter which attempts to force a specific output
format.

Fixes part 2 of of https://github.com/MusicPlayerDaemon/MPD/issues/1235
2021-08-24 13:31:13 +02:00
Max Kellermann
b904f8af03 lib/ffmpeg/Filter: add FilterContext::MakeAformat() 2021-08-24 13:30:17 +02:00
Max Kellermann
ebfbb74f9e lib/ffmpeg/DetectFilterFormat: return AudioFormat::Undefined() on EAGAIN 2021-08-24 13:30:03 +02:00
Max Kellermann
7b4225aa1f lib/ffmpeg/Filter: add ParseSingleInOut()
Merge some duplicate code.
2021-08-24 13:29:08 +02:00
Max Kellermann
71a5311b06 lib/ffmpeg/Filter: eliminate class FilterContext
Since AVFilterContext are freed automatically, this wrapper class
serves no purpose.  Let's remove it.
2021-08-24 13:04:34 +02:00
Max Kellermann
a62a35e1db lib/ffmpeg/Filter: remove FilterContext destructor
Fixes potential double-free bugs which currently did not occur because
the destructors happened to be called in the right order.
2021-08-24 12:56:05 +02:00
Max Kellermann
ca2439f595 filter/ffmpeg: pass "channel_layout" instead of "channels" to buffersrc
Fixes part 1 of https://github.com/MusicPlayerDaemon/MPD/issues/1235
2021-08-23 21:38:13 +02:00
Max Kellermann
f9a0db716a android: build with NDK r23 2021-08-23 20:58:19 +02:00
Max Kellermann
34aa67ea87 Merge remote-tracking branches 'neheb/6', 'neheb/5', 'neheb/3', 'neheb/2' and 'neheb/1' 2021-08-23 20:36:26 +02:00
Dave Hocker
18be8c3318 Fix compile error on macOS 11.15.2 (introduced by commit 30e3ef4) 2021-08-21 11:16:22 -05:00
Rosen Penev
1d7a8f992f clang-tidy: use auto
The type is duplicated otherwise

Found with modernize-use-auto

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-08-21 00:53:23 -07:00
Rosen Penev
da1783cdff clang-tidy: remove pointless const
Found with readability-const-return-type

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-08-21 00:51:21 -07:00
Rosen Penev
20d74bb07e clang-tidy: replace loop with std::all_of
Found with readability-use-anyofallof

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-08-21 00:49:36 -07:00
Rosen Penev
0f7a0b04ca replace loop with find_if
loop is wrong anyway. It's missing a break;

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-08-21 00:48:30 -07:00
Rosen Penev
40c6a214e3 unique_ptr/new to make_unique
The latter is easier to read and is the "correct" thing to do.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-08-20 23:54:14 -07:00
Samir Benmendil
cfe024ea13 command/file: return directory_uri if real_uri is unset
Prevent a segfault when accessing album art.

Fix  
2021-08-17 10:55:43 +02:00
Max Kellermann
993d85125e increment version number to 0.22.11 2021-08-17 10:55:10 +02:00
Max Kellermann
bedcf1cce5 Merge branch 'exp2' of git://github.com/neheb/MPD 2021-08-17 10:53:36 +02:00
Rosen Penev
30e3ef4c8e constexpr/std::array conversions
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-08-16 21:05:56 -07:00
Rosen Penev
4c5fea96e4 constexpr global variable conversion
Found with cppcoreguidelines-avoid-non-const-global-variables

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-08-16 20:58:21 -07:00
Rosen Penev
46600931e4 clang-tidy: use default
Found with modernize-use-default

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-08-16 20:48:28 -07:00
Max Kellermann
a2387210bf time/FileTime: move code to SystemClock.hxx 2021-08-10 19:53:53 +02:00
Max Kellermann
d7e7adb496 time/FileTime: add ChronoToFileTime() 2021-08-10 16:16:15 +02:00
Max Kellermann
45354a421c time/FileTime: preserve the FILETIME resolution
Don't truncate the FILETIME to second resolution to pass it to
std::chrono::system_clock::from_time_t(); instead, calculate the
offset between the FILETIME epoch and the
std::system_clock::time_point epoch, and use that to initialize the
time_point directly.
2021-08-10 15:16:59 +02:00
Max Kellermann
9fc3c60910 time/FileTime: add FileTimeToChronoDuration() 2021-08-10 15:13:22 +02:00
Max Kellermann
1976003e91 time/FileTime: allow negative times 2021-08-10 15:13:16 +02:00
Max Kellermann
488afc47d4 time/FileTime: use uint_least64_t 2021-08-10 15:12:49 +02:00
Max Kellermann
017814adc7 test/time/TestFileTime: new unit test 2021-08-10 15:12:34 +02:00
Max Kellermann
7f94af8b2c test/time/TestISO8601: disable on Windows for now 2021-08-10 15:07:19 +02:00
Max Kellermann
09d74f05c3 python/meson: set exe_wrapper=wine for Windows builds
Allows running the unit tests on Linux.
2021-08-10 15:04:54 +02:00
Max Kellermann
1af8694ef6 python/meson: set needs_exe_wrapper=true only for Android targets 2021-08-10 15:00:58 +02:00
Max Kellermann
b8eb9b466a python/meson: split the f.write() call and use f-strings 2021-08-10 15:00:58 +02:00
Max Kellermann
bd9e449b69 python/project: re-add support for version suffix
Got lost in commit 0f56ddb805
2021-08-10 15:00:58 +02:00
Max Kellermann
f3d67115d7 output/wasapi: check ENABLE_DSD before setting dsd_mode 2021-08-10 15:00:58 +02:00
Max Kellermann
ee6603ed38 test/meson.build: add missing dependencies on libfmt 2021-08-10 14:31:49 +02:00
Max Kellermann
0dacde32f2 output/pipewire: append output name to PW node name 2021-08-10 11:30:25 +02:00
Max Kellermann
528e05f025 output/pipewire: add config option "remote" 2021-08-10 11:28:29 +02:00
Max Kellermann
269583f5dd output/pipewire: allow specifying a target by its name 2021-08-10 11:17:16 +02:00
Max Kellermann
7c9f4f7e4f output/pipewire: create inactive stream, fill ring_buffer first
This avoids underruns at the start of playback.
2021-08-10 10:50:42 +02:00
Max Kellermann
00fd692eba output/pipewire: wait for buffer to fill before resuming 2021-08-10 10:48:25 +02:00
Max Kellermann
668c3782b2 output/pipewire: smaller ring buffer, 500ms should be enough 2021-08-10 10:43:56 +02:00
Max Kellermann
1e0af2dadf output/pipewire: add type alias for boost::lockfree::spsc_queue 2021-08-10 10:43:54 +02:00
Max Kellermann
4ea2ea2a52 output/pipewire: update nbytes after calling PcmSilence()
This was missing in commit 8a243e6e28
2021-08-10 10:43:28 +02:00
Max Kellermann
8a243e6e28 output/pipewire: call pw_stream_flush() only if really draining
If draining was not requested, generate silence instead if there is no
data in the ring buffer.

The problem is that pw_stream_flush() appears to disable the stream
permanently, even though there is no state_changed callback - the
stream state remains at PW_STREAM_STATE_STREAMING, but the stream is
defunct.  I have no idea why and I havn't found any documentation
about it.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1219
2021-08-10 06:30:58 +02:00
Max Kellermann
d33aa01000 output/pipewire: reset the "paused" flag
This was missing in commit 4d1ce7023b
2021-08-09 19:31:22 +02:00
Max Kellermann
bd893e6336 Merge tag 'v0.22.10'
release v0.22.10
2021-08-06 18:21:59 +02:00
Max Kellermann
64c39af556 release v0.22.10 2021-08-06 18:16:59 +02:00
Max Kellermann
04eb911a51 mixer/alsa: use cached values to work around rounding errors
This replaces 967af60327 with a more
effective workaround.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/822
2021-08-06 18:16:37 +02:00
Max Kellermann
351b39e0c5 mixer/alsa: skip the snd_mixer_handle_events() call in alsa_mixer_elem_callback()
snd_mixer_handle_events() has already been called by
DispatchSockets().  This way, we can also skip the exception handler.
2021-08-06 18:03:36 +02:00
Max Kellermann
3b6d4e6673 mixer/alsa: move alsa_mixer_elem_callback() into the AlsaMixer class 2021-08-06 18:01:19 +02:00
Max Kellermann
e8f328d8ad mixer/alsa: move code to GetPercentVolume() 2021-08-06 17:56:30 +02:00
Max Kellermann
5f5b5f63af mixer/alsa: move code to NormalizedToPercent() 2021-08-06 17:55:59 +02:00
Max Kellermann
ad6e303047 mixer/alsa: move code to GetNormalizedVolume() 2021-08-06 17:53:45 +02:00
Max Kellermann
b0e9538855 build/openssl: pass --cross-compile-prefix to ./Configure 2021-08-06 17:30:47 +02:00
Max Kellermann
694debd4cc build/openssl: pass RANLIB=... to "make install"
The "install_dev" target runs ranlib during installation, and this
can break the Android build.
2021-08-06 17:28:28 +02:00
Max Kellermann
0f56ddb805 python/build/libs.py: update OpenSSL to 3.0.0-beta2 2021-08-06 17:22:41 +02:00
Max Kellermann
dde77ec6bd python/build/libs.py: update CURL to 7.78.0 2021-08-06 17:20:52 +02:00
Max Kellermann
5d73eda115 doc/plugins.rst: move filter graph URL to ffmpeg.org 2021-08-06 17:20:52 +02:00
Max Kellermann
1985786ed2 db/simple: prune CUE entries from database for non-existent songs
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1019
2021-08-05 20:26:21 +02:00
Max Kellermann
8e0d39ae94 db/update/Playlist: prepend "../" only for relative URIs
Prepending "../" to absolute URIs would break them.
2021-08-05 20:19:33 +02:00
Max Kellermann
1761fb14af fs/Traits: add PathTraitsUTF8::IsAbsoluteOrHasScheme() 2021-08-05 20:09:06 +02:00
Max Kellermann
ef2fc4e6f6 db/simple/Directory: remove obsolete API doc 2021-08-05 19:05:03 +02:00
Max Kellermann
b979245d6c decoder/Bridge: call UpdateStreamTag() only if there is no pending seek
If UpdateStreamTag() gets called while an initial seek is pending, the
result will never be submitted to a MusicChunk.  By avoiding the
UpdateStreamTag() call in that case (by moving UpdateStreamTag() to
after the PrepareInitialSeek() check), the song_tag is preserved until
UpdateStreamTag() is called again from SubmitData().

This fixes missing tags in the "httpd" output.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1137
2021-08-05 18:02:45 +02:00
Max Kellermann
a74b07728e tag/Tag: add Merge() which takes Tag pointers 2021-08-05 17:36:14 +02:00
Max Kellermann
7d69cbbda7 tag/Tag: Merge() returns Tag, MergePtr() returns std::unique_ptr<Tag> 2021-08-05 17:32:23 +02:00
Constantin Fuerst
955502f881 output/oss: enable DoP
Explanation

This adds support for DOP using the PcmExport function if the macro
ENABLE_DSD is defined. If enabled within the config-file using "dop",
the boolean dop_setting will be true. If DSD input is encountered and
the setting is on, it is checked whether the oss-device supports the
required samplerate. If that is the case, dop_active is set to true
and conversion of the input is prevented. If the sample rate is not
supported, conversion to S32 is requested. When playing back, the
PcmExport is used to pack the incoming stream into PCM.  Reasoning

This is required for OSs without the required driver support for
native DSD playback that also have no ALSA. Mainly *BSD users are the
target audience for this functionality, as ALSA here is only a proxy
without full functionality.  Requirements

    DAC that supports the DOP standard
    Building with OSS, DSD and S32-Format

Supported Formats / Required PCM Formats

DSF, DFF and WavPack-DSD will work.

DSD64, 1 Channel -> S24:176.4kHz (untested, lack of time / missing samples)
DSD64, 2 Channel -> S24:352.8kHz
DSD64, 4 Channel -> S24:705.6kHz (untested, lmissing equipment)
DSD128, 1 Channel -> S24:352.8kHz (untested, lack of time / missing samples)
DSD128, 2 Channel -> S24:705.6kHz
DSD256, 1 Channel -> S24:705.6kHz (untested, lack of time / missing samples)
Changes

    inclusion of required files
    adding new domain for logging
    adding dop_satisfied private function
    adding required member variables for storing dop state and for dop-packing
    adding dop boolean parameter to many functions that are required to act a little differently when dop is active

Testing

This has been tested to work with a Sabaj Da2 on FreeBSD, where the
red status indicator LED clearly shows that DSD playback is taking
place, instead of purple for "hi-res" which is seen when converting.
Issues

I have not tested this with S24 and right now AFMT_S32_NE is
required. If not defined, ENABLE_DSD will be undef'ed. This will be
addressed in a bit, however no DAC which supports DOP but not 32Bit is
known to me. Also, AFMT_S32_NE is not defined when building on FreeBSD
which is why this is just blatantly defined in the file at the moment.
Additionally, the new dop-option is not added into any documentation
whatsoever.
2021-08-05 16:21:43 +02:00
Max Kellermann
dee5d1b87b output/oss: replace the AudioFormat field with 3 raw OSS integers
This simplifies Reopen().
2021-08-05 16:03:53 +02:00
Max Kellermann
d42342e0ba output/oss: check returned value in oss_try_ioctl() 2021-08-05 15:53:30 +02:00
Max Kellermann
8da3f8c6a7 output/oss: oss_try_ioctl() throws on EINVAL
Eliminate some duplicate code.
2021-08-05 15:47:11 +02:00
Max Kellermann
c8c553c75c pcm/Export: add method IsDopEnabled() 2021-08-05 15:35:11 +02:00
Max Kellermann
c97aabe43a Merge branch 'v0.22.x' 2021-08-05 15:17:07 +02:00
Max Kellermann
17b0ac75ca output/oss: always enable PcmExport for alsa_channel_order
We need this even when AFMT_S24_PACKED is not available, for the
correct channel order in multi-channel files.  Internally, MPD uses
FLAC channel order, but OSS uses the same channel order as ALSA.
2021-08-05 15:11:54 +02:00
Max Kellermann
bde64a13e2 tag/Builder: do not acquire tag_pool_lock if TagItem list is empty 2021-08-05 14:32:58 +02:00
Max Kellermann
96875921b7 tag/Builder: use std::swap() in move operator
This way, we save the overhead for acquiring the tag_pool_lock.
2021-08-05 14:28:37 +02:00
Cebtenzzre
551c941b5a tag/Builder: don't ignore the result of tag_pool_dup_item
Also, use RemoveAll() instead of directly clearing TagBuilder::items in
most cases, as its elements represent references that must be released.

Closes 
2021-08-05 14:25:55 +02:00
Cebtenzzre
624c77ab43 tag/Builder: another missing RemoveAll() call 2021-08-05 14:25:05 +02:00
Cebtenzzre
ba13b4b5d6 tag/Builder: use RemoveAll() to give up references 2021-08-05 14:23:48 +02:00
Cebtenzzre
4b2d9e544c tag/Pool: add [[nodiscard]] 2021-08-05 14:20:59 +02:00
Max Kellermann
97c43954e8 input/tidal: remove defunct unmaintained plugin
This plugin has been defunct for several years.  Tidal has not ever
replied to any of my emails, so they're apparently not interested in
MPD support.
2021-08-05 13:52:05 +02:00
Max Kellermann
b77acd35f7 output/winmm: fix struct/class mismatch 2021-08-05 11:40:47 +02:00
Max Kellermann
4873159872 meson.build: add "fallback" option to fmt dependency (for older Meson versions) 2021-08-05 11:28:23 +02:00
Max Kellermann
968624035c mixer/pipewire: new plugin 2021-08-05 10:57:37 +02:00
Max Kellermann
b838bf3109 output/pipewire: un-inline StateChanged() 2021-08-05 10:48:17 +02:00
Max Kellermann
4d1ce7023b output/pipewire: implement Pause() 2021-08-04 17:26:54 +02:00
Max Kellermann
52577ac87a output/pipewire: implement Cancel() properly 2021-08-04 17:24:49 +02:00
Max Kellermann
9fa3984a2f input/icy: adjust offset at end of stream in Read()
ProxyInputStream::Read() assigns the `offset` field, which is the
wrong offset because it does not consider Icy metadata removed from
the stream.  Therefore, after every ProxyInputStream::Read() call,
IcyInputStream::Read() needs to override this offset.  This was
missing at the end of the stream, when Read()==0.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1216
2021-08-02 16:40:04 +02:00
Max Kellermann
239698cb5a output/pipewire: set channel positions 2021-07-30 15:55:37 +02:00
Max Kellermann
e55de6e9f0 output/pipewire: implement Drain() 2021-07-30 15:28:01 +02:00
Max Kellermann
cfaf2ed03c output/pipewire: move code to CheckThrowError() 2021-07-30 15:24:20 +02:00
Max Kellermann
6015960871 output/pipewire: reset the "interrupted" flag in Cancel()
This fixes seeking.
2021-07-30 15:20:32 +02:00
Max Kellermann
26328cc915 output/pipewire: detect connection errors 2021-07-30 14:31:06 +02:00
Max Kellermann
cd512f0b40 output/pipewire: replace usleep() with with pw_thread_loop_wait() 2021-07-30 14:31:04 +02:00
Max Kellermann
be14dd59a8 output/pipewire: remove obsolete TODO comment 2021-07-30 13:50:55 +02:00
Max Kellermann
97e5787ff7 output/pipewire: call libpipewire only while holding the lock
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1210
2021-07-30 13:49:22 +02:00
Max Kellermann
6975d3ca4b output/pipewire: switch from pw_main_loop to pw_thread_loop
We need this for pw_thread_loop_lock().
2021-07-30 13:42:59 +02:00
Max Kellermann
cdca27e6bb decoder/Bridge: fix libfmt string 2021-07-30 13:32:27 +02:00
Max Kellermann
5355335f19 db/simple/ExportedSong: check src.OwnsTag(), not this->OwnsTag()
this->OwnsTag() accesses fields that are not yet initialized.
2021-07-30 13:10:09 +02:00
Sam Bazley
5b775ca5b4 decoder/ffmpeg: check if long_name is not null 2021-07-28 16:05:15 +01:00
Jacob Vosmaer
ea95da3b1a archive/meson.build: add missing libfmt dependency 2021-07-26 23:26:07 +02:00
Sam Bazley
57687779be Android: add option to pause MPD when headphones disconnect 2021-07-26 19:02:47 +01:00
Max Kellermann
64fa76c568 command/file: support "albumart" for virtual tracks in CUE sheets
Instead of checking for "cover.jpg" in the virtual directory
representing the CUE sheet, check its enclosing directory.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1206
2021-07-16 09:04:35 +02:00
Max Kellermann
19a44076cf command/file: pass directory uri to read_stream_art() 2021-07-16 08:31:58 +02:00
Max Kellermann
809a18913a fs/Traits: add overload GetParent(string_view) 2021-07-16 08:30:34 +02:00
Max Kellermann
5eab2d96f4 output/winmm: fix struct/class mismatch 2021-07-16 08:30:34 +02:00
Max Kellermann
716784f632 increment version number to 0.22.10 2021-07-16 07:23:00 +02:00
Khem Raj
d39b11ba5d include <utility> for std::forward
Fixes
../git/src/Log.hxx:121:42: error: no member named 'forward' in namespace 'std'
        LogFormat(LogLevel::ERROR, e, fmt, std::forward<Args>(args)...);

Signed-off-by: Khem Raj <raj.khem@gmail.com>
2021-06-29 19:34:28 -07:00
Max Kellermann
b29a43b4d7 decoder/mad, ...: more libfmt logging 2021-06-25 20:52:08 +02:00
Max Kellermann
f60a42e0b6 Log, client/Response: adapt to libfmt 8.0.0 API changes 2021-06-25 20:29:25 +02:00
Max Kellermann
85b0029ba2 meson.build: add missing dependencies on libfmt 2021-06-25 20:28:54 +02:00
Max Kellermann
0e0f46a1e0 Log: remove unused Format functions 2021-06-24 21:40:12 +02:00
Max Kellermann
6f539cfcd6 Partition, ...: use libfmt for logging 2021-06-24 21:40:11 +02:00
Max Kellermann
0185d58a2b Log: add libfmt support 2021-06-24 21:14:42 +02:00
Naglis Jonaitis
eb630ca655 doc/user.rst: rectify admin permission
Updating the database no longer requires the `admin` permission, only
`control` is needed (changed in 2abad0f479).

See also: 
2021-06-24 16:44:38 +02:00
Max Kellermann
d7df5e1c90 LogBackend: pass std::string_view to Log() 2021-06-24 13:43:13 +02:00
Max Kellermann
e4e4576a39 Merge tag 'v0.22.9'
release v0.22.9
2021-06-23 21:02:06 +02:00
Max Kellermann
18628bf89e release v0.22.9 2021-06-23 20:56:13 +02:00
Yetangitu
2052b461af Fix android build error when confronted with package versions ending in +revision_information
The script seems to assume package version numbers always end in numeric versions with an optional alpha-suffix. Alas, were it only so simple... Sometimes the package is called fizzbang-1.2.3+release_info in which case the build fails. No more!

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1177
2021-06-23 20:53:46 +02:00
Max Kellermann
5019bdcd52 TagAny: invoke ScanGenericTags() on remote files
This fixes reading ID3 tags on remote files with the commands
"readcomments" and "readpicture".

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1180
2021-06-23 20:49:30 +02:00
Naglis Jonaitis
8be0bcbdb9 doc/plugins.rst: mention default libsamplerate type 2021-06-23 15:51:31 +02:00
Naglis Jonaitis
af72a22ed8 doc/user.rst: document restore_paused 2021-06-23 15:50:41 +02:00
Naglis Jonaitis
6ed9668fea doc, README.md: update IRC server name/URL 2021-06-23 15:48:42 +02:00
Max Kellermann
175d2c6d29 Main: use AtScopeExit() to call ZeroconfDeinit()
Make sure that ZeroconfDeinit() gets called even if startup fails with
an exception.  Fixes an assertion failure because an Avahi TimerEvent
is still active.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1192
2021-06-22 20:31:45 +02:00
Naglis Jonaitis
f789451007 doc/plugins.rst: fix PipeWire link 2021-06-19 21:29:37 +03:00
Max Kellermann
36680607d0 meson.build: use some warning options for plain C as well 2021-06-01 11:14:28 +02:00
Max Kellermann
fc54877c6b meson.build: merge duplicate warning flags to test_common_flags 2021-06-01 11:04:27 +02:00
Rosen Penev
6af7be4a45 add constexpr
Found with cppcoreguidelines-interfaces-global-init

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-05-31 13:45:39 -07:00
Max Kellermann
ab487b9a99 Android: use startForegroundService() in Android 8+
Fixes the error:

 IllegalStateException: Not allowed to start service Intent { cmp=org.musicpd/.Main (has extras) }: app is in background
2021-05-31 20:45:31 +02:00
Max Kellermann
ac59ec34f9 decoder/ffmpeg: fix build failure with FFmpeg 3.4
av_demuxer_iterate() was added in libavformat 58.9.100.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1178
2021-05-31 18:10:06 +02:00
Max Kellermann
82da57b7ce decoder/ffmpeg: suppress -Wunused with libavformat<58.6.100 2021-05-31 16:49:48 +02:00
Max Kellermann
aa6dac9bd2 db/proxy: suppress -Wunused with libmpdclient<2.12 2021-05-31 16:49:08 +02:00
Rosen Penev
220d2bf026 clang-tidy: add explicit deleted constructors
Found with cppcoreguidelines-special-member-functions

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-05-30 22:47:20 -07:00
Rosen Penev
9ef1cf15a9 clang-tidy: default virtual destructors
Found with cppcoreguidelines-special-member-functions

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-05-30 22:46:46 -07:00
Max Kellermann
679b3bc00f output/print, command/player: print bool as integer
Fixes protocol breakage after commit 0440c41cba

libfmt is too clever for the MPD protocol!

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1175
2021-05-28 18:02:00 +02:00
Max Kellermann
1f9e32c35e NEWS: fix typo 2021-05-28 15:46:38 +02:00
Max Kellermann
36410daaa4 queue/PlaylistEdit: fix inverted check
Regression by commit 471c37be59

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1135
2021-05-28 13:56:01 +02:00
Max Kellermann
38bfef7d0b Merge branch 'add-openmpt-decoder' of git://github.com/GrimReaperFloof/MPD 2021-05-28 13:53:43 +02:00
GrimReaperFloof
724754f16c Fix std::to_string warning for booleans in openmpt decoder 2021-05-27 20:47:45 +02:00
Max Kellermann
4d32454697 android/LogListener: pass formatted message to OnLog() 2021-05-27 16:21:36 +02:00
Max Kellermann
4db882f666 android/LogListener: cache the jmethodID 2021-05-27 16:14:43 +02:00
Max Kellermann
a83bf97b98 android/LogListener: un-inline the constructor 2021-05-27 16:13:22 +02:00
Max Kellermann
262e1957b7 lib/icu/Converter: use libfmt 2021-05-27 16:09:56 +02:00
Max Kellermann
792411384d protocol/ArgParser: add function MakeArgError()
Replaces FormatProtocolError().
2021-05-27 16:01:44 +02:00
Max Kellermann
78b0ff83e8 client/Response: include cleanup 2021-05-27 15:59:06 +02:00
Max Kellermann
23613355f3 client/Response: remove unused method FormatV() 2021-05-27 15:16:35 +02:00
Max Kellermann
0d97eba7a5 client/Response: refactor FormatError() to use libfmt 2021-05-27 15:15:47 +02:00
Max Kellermann
18efda719e client/Response: remove unused method Format() 2021-05-27 15:14:54 +02:00
Max Kellermann
42239a30eb client/Response: use Fmt() in FormatError() 2021-05-27 15:05:42 +02:00
Max Kellermann
a26bf261a9 input/last: call Close() in Open()
Prevents a possible bug which occurs when the caller-provided open()
function throws; then the "uri" field is never set.
2021-05-27 14:04:28 +02:00
Max Kellermann
c692286c67 input/last: clear "uri" field in Close()
Prevent false negative after the stream was closed automatically after
20 seconds.
2021-05-27 14:03:33 +02:00
GrimReaperFloof
43a9dc7082 Add note about emulate_amiga_type requiring libopenmpt 0.5 2021-05-26 23:43:38 +02:00
GrimReaperFloof
6f64fa070d Add repeat count setting to openmpt decoder 2021-05-26 23:43:38 +02:00
GrimReaperFloof
dc5b9d989b Backwards compatibility with older libopenmpt versions than 0.5 2021-05-26 23:43:38 +02:00
GrimReaperFloof
9e407f5989 Change WritableBuffer<uint8_t> to AllocatedArray<std::byte> 2021-05-26 23:43:38 +02:00
GrimReaperFloof
fec6aac0f1 Code deduplication: move mod_loadfile() into ModCommon.cxx 2021-05-26 23:43:38 +02:00
GrimReaperFloof
541c31c879 Add openmpt decoder plugin 2021-05-26 23:43:38 +02:00
Max Kellermann
4ee0a06e18 Merge branch 'v0.22.x' 2021-05-26 13:15:29 +02:00
Max Kellermann
3775766605 NEWS: mention new FFmpeg/ID3v2 tags 2021-05-26 13:07:03 +02:00
Max Kellermann
38e24208f6 decoder/ffmpeg: support the tags "album-sort", "artist-sort" 2021-05-26 13:04:47 +02:00
Max Kellermann
fbaedf2262 decoder/ffmpeg: support the "sort_album" tag
From libavformat/mov.c.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1173
2021-05-26 13:03:47 +02:00
Max Kellermann
8f3341cefb decoder/ffmpeg: add comment 2021-05-26 13:03:41 +02:00
Max Kellermann
4ec4bab3a9 decoder/ffmpeg: remove "year" tag
This mapping was added 11 years ago in commit 766b9fd453, but FFmpeg
doesn't appear to support it.
2021-05-26 13:03:27 +02:00
Max Kellermann
6d567bcd35 decoder/ffmpeg: fix ArtistSort and AlbumArtistSort mapping
These were added 11 years ago in commit 766b9fd453, but I cannot find
any evidence in the FFmpeg repository that these names were ever
supported.  This commit adds the tags as they are currently present in
libavformat/mov.c.
2021-05-26 13:03:26 +02:00
Max Kellermann
4f75eb9bfe output/pipewire: remove unreachable "return" statement 2021-05-26 11:57:57 +02:00
Max Kellermann
d2bd12822f Merge branch 'v0.22.x' 2021-05-26 11:57:41 +02:00
Max Kellermann
363d9f0180 db/update/Walk: load all .mpdignore files of all parent directories
When updating everything, this did work, but if updating only a
subdirectory, the ".mpdignore" in the parents were not used.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1172
2021-05-25 22:42:44 +02:00
Max Kellermann
db0682a469 db/update/Walk: move code to LoadExcludeList() 2021-05-25 22:38:01 +02:00
Max Kellermann
7a6823dcdf zeroconf/AvahiPoll: the struct timeval is an absolute time point
Fixes broken libavahi-client timeouts.
2021-05-25 22:25:45 +02:00
Max Kellermann
bce144a232 zeroconf/AvahiPoll: move code to Schedule() 2021-05-25 22:23:55 +02:00
Max Kellermann
0cef84cac6 zeroconf/AvahiPoll: rename "timer" to "event" 2021-05-25 22:23:55 +02:00
Max Kellermann
56c0733b42 meson.build: disable -Wsuggest-override with GCC 8 2021-05-25 22:23:55 +02:00
Max Kellermann
0b0acb3981 meson.build: add more C++ warning flags 2021-05-25 22:03:49 +02:00
Max Kellermann
1375dcc4ec meson.build: sort warning options 2021-05-25 21:49:03 +02:00
Max Kellermann
6aeb0e335b meson.build: add comment for -Wno-non-virtual-dtor 2021-05-25 21:48:19 +02:00
Max Kellermann
c1e2537851 meson.build: add comment for clang-only warning options 2021-05-25 21:45:39 +02:00
Max Kellermann
8c690fb737 decoder/mad: move variable declaration into "case" 2021-05-25 21:34:09 +02:00
Max Kellermann
dad1c21b59 zeroconf/avahi: move variable declaration into "case" 2021-05-25 21:34:09 +02:00
Max Kellermann
dd10b2bd61 meson.build: remove warning options implied by -Wall -Wextra 2021-05-25 21:24:44 +02:00
Max Kellermann
48c7c540df meson.build: use add_project_arguments() instead of add_global_arguments()
Don't propagate MPD-specific compiler flags to subprojects.
2021-05-25 21:08:06 +02:00
Max Kellermann
281270cd2a meson.build: remove unused variables common_cflags, common_cxxflags 2021-05-25 21:07:05 +02:00
Max Kellermann
02502514f6 meson.build: require clang 7 (remove bug workaround) 2021-05-25 21:06:55 +02:00
Max Kellermann
1bc02123f9 meson.build: remove "-pedantic", implied by Meson
Meson adds "-Wpedantic" in warning_level 3 (which is MPD's default).
2021-05-25 21:01:15 +02:00
Max Kellermann
3488a47c41 subprojects/sqlite3.wrap: add SQLite wrap 2021-05-25 20:51:03 +02:00
Max Kellermann
fd82d67678 sticker/Database: pass NarrowPath to sqlite3_open()
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1171
2021-05-25 18:45:45 +02:00
Max Kellermann
e66c12105b lib/sqlite/meson.build: add missing external dependency on libsqlite 2021-05-25 18:41:43 +02:00
Max Kellermann
8a9d678bac Merge branch 'v0.22.x' 2021-05-25 18:21:42 +02:00
Namkhai B
dbe12a6b90 util/RuntimeError: Disable format-security for gcc
Fixes building under GCC 11
2021-05-25 18:19:19 +02:00
Max Kellermann
0440c41cba client/Response: add method Fmt() based on libfmt 2021-05-25 16:01:56 +02:00
Max Kellermann
a9c704b76e meson.build: libfmt integration 2021-05-25 15:48:49 +02:00
Philippe Antoine
d3a680cc87 meson: set only sanitizers for fuzzer when unspecified
That is when meson option b_sanitize is not used
2021-05-24 09:03:16 +02:00
Max Kellermann
62fc4d5cf4 increment version number to 0.22.9 2021-05-24 09:03:07 +02:00
Max Kellermann
0cca1b138c Merge tag 'v0.22.8'
release v0.22.8
2021-05-22 17:35:30 +02:00
Max Kellermann
d3576a1b71 input/last: add nullptr check to Open(), fixes assertion failure
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1165
2021-05-21 19:30:36 +02:00
Max Kellermann
96707c0426 Merge tag 'v0.22.7'
release v0.22.7
2021-05-19 18:43:19 +02:00
Max Kellermann
e016cc8940 lib/upnp/meson.build: auto-disable UPnP without CURL/expat 2021-05-19 14:37:13 +02:00
GrimReaperFloof
34f636ffc3 Change WritableBuffer<uint8_t> to AllocatedArray<std::byte> 2021-05-19 09:54:06 +02:00
GrimReaperFloof
a134f692bf Code deduplication: move mod_loadfile() into ModCommon.cxx 2021-05-19 09:53:39 +02:00
Max Kellermann
d747576793 Merge branch 'npu' of git://github.com/neheb/MPD 2021-05-19 09:48:12 +02:00
Max Kellermann
d9578f6427 Merge branch 'flac-ogg' of git://github.com/jprjr/VGMPD 2021-05-19 09:33:42 +02:00
Max Kellermann
b2cec7a0a3 Merge branch 'v0.22.x' 2021-05-19 08:09:05 +02:00
Max Kellermann
85db2d6704 db/proxy: split search into chunks to avoid exceeding the output buffer
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1130
2021-05-19 08:04:50 +02:00
Max Kellermann
22ebb2bdd5 db/proxy: send "window" as separate parameter to SendConstraints() 2021-05-19 07:51:48 +02:00
Max Kellermann
e108568082 db/proxy: require libmpdclient 2.11 or later
Remove lots of #ifdefs.
2021-05-19 07:46:42 +02:00
Max Kellermann
360381e65d doc/plugins.rst: mention the minimum MPD version 2021-05-19 07:46:42 +02:00
Max Kellermann
3ead778664 doc/plugins.rst: add libmpdclient website link 2021-05-19 07:45:47 +02:00
Max Kellermann
4fc08e39b4 db/proxy: require MPD 0.20 or later
Allows using ranges, always.  This is required to fix
https://github.com/MusicPlayerDaemon/MPD/issues/1130 without adding
more runtime conditionals.
2021-05-19 07:41:04 +02:00
Max Kellermann
a2bdac571a Merge branch 'v0.22.x' 2021-05-17 19:33:15 +02:00
John Regan
87fa6bca54 flac encoder: enable Ogg FLAC and Ogg chaining
refactors GenerateOggSerial into a generic GenerateSerial
utility, under the util lib.

libFLAC may be encoded without Ogg support. If Ogg support is disabled,
libFLAC will still export Ogg-related methods (like setting a serial
number), and throw a runtime error when initializing an Ogg stream.

GenerateOggSerial does not depend on libogg. Refactoring it into
a generic GenerateSerial prevents having to add build-time checks
for libogg within the FLAC encoder plugin.
2021-05-15 11:31:01 -04:00
Max Kellermann
c3226a3195 doc/conf.py: Copyright 2021 2021-05-11 17:57:14 +02:00
Max Kellermann
51671af5a4 doc/conf.py: move "~git" suffix to release 2021-05-11 17:52:24 +02:00
Max Kellermann
2908f6565b doc/conf.py: update version number to 0.23~git 2021-05-11 17:49:26 +02:00
GrimReaperFloof
a0334d1d94 Add resampling mode setting to modplug decoder 2021-05-07 19:48:52 +02:00
Max Kellermann
423f2df5e0 pcm/Volume: drop support for GCC older than 8 2021-04-20 20:01:43 +02:00
Max Kellermann
0122dc8452 util/BindMethod: drop support for GCC older than 7 2021-04-20 20:01:08 +02:00
Max Kellermann
95ad1b0cc6 use [[gnu::pure]] instead of gcc_pure
This is semi-standard and doesn't require the util/Compiler.h header.
2021-04-06 14:09:21 +02:00
Max Kellermann
52f46b94e9 util/AllocatedString: add concatenating constructor 2021-04-06 14:06:16 +02:00
Max Kellermann
e07e0bc9c1 util/AllocatedArray: include cleanup 2021-04-06 14:05:29 +02:00
Max Kellermann
4a1c231734 net/SocketError: use constexpr 2021-04-06 13:59:58 +02:00
Max Kellermann
fd0e958e95 net/SocketError: use auto 2021-04-06 13:58:46 +02:00
Max Kellermann
3d814115c8 net/SocketError: change "const" to "pure" 2021-04-06 13:58:32 +02:00
Max Kellermann
ca726a0110 util/StringBuffer: make capacity() static
This allows using it in constant expressions.
2021-04-06 13:46:07 +02:00
Max Kellermann
e01710cbd1 util/DereferenceIterator: simplify operator->()
This makes it compatible with containers storing std::unique_ptr.
2021-04-06 13:45:32 +02:00
Max Kellermann
c87a4a7d08 util/DereferenceIterator: fix static_cast in operator->() 2021-04-06 13:45:10 +02:00
Max Kellermann
b59170b702 Java/Exception: simplify RethrowException() 2021-04-06 13:35:59 +02:00
Max Kellermann
a237db556a java/File: add non-static GetAbsolutePath() overload 2021-04-06 13:35:59 +02:00
Max Kellermann
285ba54fe5 java/String: remove unnecessary env parameter 2021-04-06 13:35:59 +02:00
Max Kellermann
ee86434a89 java/String: add const 2021-04-06 13:35:59 +02:00
Max Kellermann
95d5efbfe6 java/Ref: add nullptr comparison 2021-04-06 13:35:59 +02:00
Max Kellermann
c33f206ce8 java/Ref: add nullptr constructor 2021-04-06 13:29:35 +02:00
Max Kellermann
2d95ac2e94 Java/String: inherit the super class constructor 2021-04-06 13:29:24 +02:00
Max Kellermann
f58c14a74a Java: no namespace indent 2021-04-06 13:29:13 +02:00
Max Kellermann
a52ce7bb7b java/Ref: add move operator 2021-04-06 13:27:11 +02:00
Max Kellermann
16d187b7ed java/Ref: remove const, add default initialiser 2021-04-06 13:26:54 +02:00
Max Kellermann
296ec4d07c java/Ref: add nullptr check to destructor
May allow the compiler to optimize calls away.
2021-04-06 13:26:29 +02:00
Max Kellermann
6e58fd1583 lib/curl/Multi: reword API documentation 2021-03-29 20:19:21 +02:00
Max Kellermann
c5fec4ac2a lib/curl/Multi: move operator bool() down 2021-03-29 20:19:21 +02:00
Max Kellermann
fe2ca1ddef lib/curl/Multi: rename parameters 2021-03-29 20:19:19 +02:00
Jean-Francois Dockes
e960626804 Add npupnp support
libnpupnp is a C++ modification of libupnp.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-03-26 14:14:58 -07:00
Simon Persson
7dd2dce6ad Support new tags in proxy plugin
Add support for the following tags when using the proxy database plugin:
WORK
CONDUCTOR
LABEL
GROUPING
MUSICBRAINZ_WORKID
COMPOSERSORT
ENSEMBLE
MOVEMENT
MOVEMENTNUMBER
LOCATION
2021-03-24 20:59:54 +01:00
Max Kellermann
a7ba10423d Merge branch 'v0.22.x' 2021-03-13 08:41:10 +01:00
Simon Persson
8f1e7385b7 Add tags relevant to classical music.
This commit adds some tags that are (mostly) interesting for listeners
of classical music.

Ensemble
--------
This is an ensemble that is playing the music, such as Wiener
Philharmoniker. The tag can be used to distinguish the ensemble from the
conductor, composer, soloist, and ensemble, that are generally all in
the "ARTIST" tag.

Movement
-------
The movement number and movement (name) of this track, i.e.  "II" and
"Allegro".

ComposerSort
------------
Allows us to look for Beethoven's 9th under B, for Beethoven, not L for
Ludwig.

Location
--------
This is the location of the recording, e.g. "Wiener Musikverein".
2021-03-10 21:24:25 +01:00
Max Kellermann
25354b9d8c Merge branch 'v0.22.x' 2021-03-10 21:06:12 +01:00
Max Kellermann
ee720064a7 Merge branch 'v0.22.x' 2021-03-05 19:41:17 +01:00
Max Kellermann
e1b62fb90d Merge branch 'v0.22.x' 2021-03-05 19:33:46 +01:00
Max Kellermann
422cf5f182 Merge branch 'v0.22.x' 2021-03-05 16:05:56 +01:00
Max Kellermann
ef1acb4e2f Merge branch 'v0.22.x' 2021-03-04 18:56:29 +01:00
Max Kellermann
d4bbb8c851 Merge branch 'struc' of git://github.com/neheb/MPD 2021-03-04 17:50:53 +01:00
Max Kellermann
428f769c38 output/pipewire: new output plugin
Very rough draft.  Barely works.
2021-03-03 18:44:39 +01:00
Max Kellermann
133c8834df output/httpd: update API documentation 2021-03-02 18:24:57 +01:00
Max Kellermann
99217593bf test/util/TestIntrusiveList: add AutoUnlink test 2021-03-02 18:15:37 +01:00
Max Kellermann
1c6e4a2b18 test/util/TestIntrusiveList: new unit test 2021-03-02 18:12:15 +01:00
Max Kellermann
a6eb264770 util/IntrusiveList: add type alias "Hook"
By casting to SafeLinkIntrusiveListHook if appropriate, this fixes a
bug in the erase() method, where erase() calls
IntrusiveListHook::unlink() instead of
SafeLinkIntrusiveListHook::unlink().
2021-03-02 17:51:50 +01:00
Max Kellermann
f5f296b13a event/TimerWheel: add a "ready" list as a special case
This reduces delays of zero-duration timers from up to 1 second to
zero.  libavahi-client schedules zero-duration timers often.
2021-03-02 17:14:33 +01:00
Max Kellermann
0091c4e12b util/Exception: add FindNested() 2021-03-02 17:13:29 +01:00
Max Kellermann
80172e17ac util/Exception: remove redundant std::exception catch clause
The "std::nested_exception" catch block already covers this perfectly.
2021-03-02 17:13:21 +01:00
Max Kellermann
2d96b05403 test/util/TestException: add CheckFindRetrowNested() unit test 2021-03-02 17:12:43 +01:00
Max Kellermann
ec0c1f0d02 util/Exception: fix comment typo 2021-03-02 17:12:20 +01:00
Max Kellermann
946b3c1f80 util/IntrusiveList: add method erase_and_dispose() 2021-02-25 14:12:47 +01:00
Max Kellermann
a0dc398f36 util/IntrusiveList: erase() returns an iterator 2021-02-25 14:12:39 +01:00
Max Kellermann
b54d2d984a util/IntrusiveList: use ToHook() in erase() 2021-02-25 14:12:10 +01:00
Max Kellermann
4ab73f9de9 util/IntrusiveList: add missing ToNode() cast in iterator_to() 2021-02-25 14:10:55 +01:00
Max Kellermann
5ebe23e4bb db/upnp/Discovery: use class IntrusiveList 2021-02-24 20:39:42 +01:00
Max Kellermann
aa227cded1 input/qobuz: use class IntrusiveList 2021-02-24 20:31:47 +01:00
Max Kellermann
e406bdbb80 input/tidal: use class IntrusiveList 2021-02-24 20:31:13 +01:00
Max Kellermann
1048f23680 util/IntrusiveList: add hook class SafeLinkIntrusiveListHook
Similar to boost::intrusive::safe_link.
2021-02-24 20:29:16 +01:00
Max Kellermann
8fe8f09027 util/IntrusiveList: add noexcept 2021-02-24 20:17:28 +01:00
Max Kellermann
78670c0941 util/IntrusiveList: add constexpr 2021-02-24 20:16:54 +01:00
Max Kellermann
34f735890e output/snapcast: remove obsolete TODO comment 2021-02-24 17:05:14 +01:00
Max Kellermann
f08810b202 output/snapcast: add missing #ifdef HAVE_ZEROCONF 2021-02-24 17:04:15 +01:00
Max Kellermann
7a68775e6c output/snapcast: Zeroconf support 2021-02-24 17:03:30 +01:00
Max Kellermann
e4fccc85c8 Main: move Zeroconf to the I/O thread
This will allow using Zeroconf in output plugins (preparing for
Snapcast with Zeroconf).
2021-02-24 16:29:33 +01:00
Max Kellermann
2efa142ec9 output/init: use the real-time I/O thread only for the ALSA plugin 2021-02-24 16:09:11 +01:00
Max Kellermann
29b49dd630 zeroconf/{bonjour,avahi}: pass service_type as parameter 2021-02-24 15:11:11 +01:00
Max Kellermann
9d6bf7e720 test/run_avahi: refactor to RunZeroconf (both avahi and bonjour) 2021-02-24 15:09:10 +01:00
Max Kellermann
5f34508aae zeroconf/Glue: move code to the ZeroconfHelper constructor 2021-02-24 15:09:10 +01:00
Max Kellermann
2d8ecd561b zeroconf: return a publisher object 2021-02-24 15:09:10 +01:00
Max Kellermann
2059195ae9 zeroconf: add dependency on event_dep 2021-02-24 15:09:10 +01:00
Max Kellermann
d89856f77b zeroconf/avahi/Helper: make class SharedAvahiClient final 2021-02-24 15:09:10 +01:00
Max Kellermann
975d5be046 zeroconf/avahi/Init: return a std::unique_ptr<AvahiHelper> 2021-02-24 14:41:37 +01:00
Max Kellermann
b01ef1b9a6 zeroconf/Bonjour: return a std::unique_ptr<BonjourHelper> 2021-02-24 14:25:06 +01:00
Max Kellermann
ceb76b6a82 zeroconf/Bonjour: pass a context pointer to the callback 2021-02-24 14:18:35 +01:00
Max Kellermann
a7e697b588 zeroconf/Bonjour: add const 2021-02-24 14:18:17 +01:00
Max Kellermann
3ecd918442 zeroconf/Bonjour: move the DNSServiceRegister() call to the constructor 2021-02-24 14:17:59 +01:00
Max Kellermann
4fbdb3a2d5 zeroconf/Bonjour: convert the callback function to a method 2021-02-24 14:15:26 +01:00
Max Kellermann
0157643667 zeroconf/Glue: add noexcept 2021-02-24 14:15:26 +01:00
Max Kellermann
fe741bd767 zeroconf/Glue: allow ZeroconfInit() to throw 2021-02-24 14:15:04 +01:00
Max Kellermann
06b9bdba2c zeroconf/Bonjour: disallow copying 2021-02-24 13:53:36 +01:00
Max Kellermann
bd0aa74bdd zeroconf/Bonjour: rename class BonjourMonitor to BonjourHelper 2021-02-24 13:53:35 +01:00
Max Kellermann
47461df59c zeroconf/Bonjour: don't call DNSServiceRefDeallocate() if DNSServiceRegister() fails
According to
https://developer.apple.com/documentation/dnssd/1804733-dnsserviceregister
the DNSServiceRef is initialized only if DNSServiceRegister() returns
kDNSServiceErr_NoError.  The faulty error handling code could
therefore crash.
2021-02-24 13:49:03 +01:00
Max Kellermann
04d5588fe5 zeroconf/Zeroconf*: drop prefix from file names 2021-02-24 13:33:59 +01:00
Max Kellermann
40d061621b zeroconf/Avahi: remove useless log messages 2021-02-24 13:33:58 +01:00
Max Kellermann
a312629aad zeroconf: pass global port to init function 2021-02-24 06:40:26 +01:00
Max Kellermann
d527d4b530 zeroconf/avahi/Publisher: new class, replacing lots of code from ZeroconfAvahi.cxx 2021-02-23 22:07:57 +01:00
Max Kellermann
978d2638d8 zeroconf/avahi/Client: new class, replacing lots of code from ZeroconfAvahi.cxx 2021-02-23 21:53:07 +01:00
Max Kellermann
cfcafdf822 zeroconf/avahi: add class AvahiGlue 2021-02-23 21:53:00 +01:00
Max Kellermann
07865d0707 zeroconf/avahi/Poll: move to namespace Avahi 2021-02-23 21:41:16 +01:00
Max Kellermann
1ac16516a1 event/TimerList: add option to avoid the Boost dependency 2021-02-22 23:35:16 +01:00
Max Kellermann
75e8795e3f util/IntrusiveList: add method insert() 2021-02-22 23:32:51 +01:00
Max Kellermann
4912466d50 util/IntrusiveList: add method erase() 2021-02-22 23:32:47 +01:00
Max Kellermann
664674913e event/Loop: include cleanup 2021-02-22 23:32:39 +01:00
Max Kellermann
31e3658823 Merge branch 'v0.22.x' 2021-02-22 23:25:07 +01:00
Max Kellermann
abd416735d output/snapcast: implement SendTag() 2021-02-22 23:20:56 +01:00
Max Kellermann
6090bd2095 lib/yajl/Gen: new class 2021-02-22 23:12:08 +01:00
Max Kellermann
1777592ec0 lib/yajl/Handle: fix API documentation 2021-02-22 22:56:52 +01:00
Max Kellermann
8e8fbe14b1 output/snapcast: implement Drain() 2021-02-22 22:49:32 +01:00
Max Kellermann
a8a39b6a38 output/snapcast: queue chunks 2021-02-22 22:41:31 +01:00
Max Kellermann
f84cb6de5e output/snapcast/client: remove obsolete TODO comment
See commit dfc67c45c7
2021-02-22 22:39:32 +01:00
Max Kellermann
dfc67c45c7 output/snapcast: calculate the latency for TIME responses 2021-02-22 21:57:20 +01:00
Max Kellermann
e875da5d38 output/snapcast/protocol: swap "received" and "sent"
Snapcast's protocol documentation is wrong, see
https://github.com/badaix/snapcast/pull/811
2021-02-22 21:42:42 +01:00
Max Kellermann
9b9522e3f5 zeroconf/avahi/Poll: rename internal variables 2021-02-22 15:01:01 +01:00
Max Kellermann
87963685fb zeroconf/avahi/Poll: use C++11 initializer 2021-02-22 15:00:20 +01:00
Max Kellermann
0405a57f26 zeroconf/avahi/Poll: make EventLoop the first parameter 2021-02-22 14:52:21 +01:00
Max Kellermann
f29c69d6a9 zeroconf/avahi/Poll: rename timer to event 2021-02-22 14:39:28 +01:00
Max Kellermann
7ec4de841e zeroconf/avahi/Poll: add constexpr 2021-02-22 14:34:32 +01:00
Max Kellermann
1f08d2d03c zeroconf/avahi/Poll: add noexcept 2021-02-22 14:31:37 +01:00
Max Kellermann
c1a695d1ac zeroconf/avahi/Poll: add method GetEventLoop() 2021-02-22 14:31:10 +01:00
Max Kellermann
ec05056e38 zeroconf/avahi/Poll: forbid copying 2021-02-22 14:26:08 +01:00
Max Kellermann
c0b9339d31 zeroconf/AvahiPoll: move to lib/avahi/ 2021-02-22 14:24:00 +01:00
Max Kellermann
00a1731085 Merge branch 'timeout_in_second' of git://github.com/mxjeff/MPD 2021-02-20 20:11:38 +01:00
kaliko
6e3da00874 MPD_TIMEOUT is in seconds
At least I believe libmpdclient is reading env. var. as second:
https://github.com/MusicPlayerDaemon/libmpdclient/blob/master/src/settings.c#L142

ncmpc manual also mentions MPD_TIMEOUT being is second.
2021-02-20 15:22:18 +01:00
kaliko
38df01b266 Add password use with MPD_HOST 2021-02-20 15:21:15 +01:00
Max Kellermann
c729f16dcd song/DetachedSong: copy the AudioFormat from LightSong
Enables the "Format" row in "playlistinfo" responses.

https://github.com/MusicPlayerDaemon/MPD/issues/1094
2021-02-18 22:16:11 +01:00
Max Kellermann
81d0c04ed4 song/DetachedSong: add noexcept 2021-02-18 22:01:52 +01:00
Max Kellermann
0924b63e10 event/TimerWheel: add empty flag to optimize a common case 2021-02-17 19:52:45 +01:00
Max Kellermann
ce6afe9379 output/httpd/Page: convert to type alias on AllocatedArray 2021-02-17 18:01:27 +01:00
Max Kellermann
6f04b2230a output/httpd/Page: use std::byte 2021-02-17 17:54:38 +01:00
Max Kellermann
8d90b831e1 output/snapcast/Timestamp: drop static 2021-02-17 14:39:20 +01:00
Max Kellermann
d4710604c4 doc/plugins.rst: fix typo 2021-02-17 14:39:06 +01:00
Max Kellermann
9c8da03c5c output/snapcast: new output plugin
New experimental code, first draft - it works, but there's a lot left
to do.  Just look at all the TODO comments.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/975
2021-02-17 14:25:23 +01:00
Max Kellermann
85adefd9a4 encoder/wave: remove constexpr because memcpy() is not allowed 2021-02-17 14:19:02 +01:00
Max Kellermann
22804cfbe8 Merge branch 'patch-2' of git://github.com/fschlich/MPD 2021-02-17 07:25:06 +01:00
Max Kellermann
8a4b88a59d encoder/wave: use the structs from RiffFormat.hxx 2021-02-16 19:50:52 +01:00
Max Kellermann
d2371af120 encoder/wave: add static_assert on sizeof(WaveHeader) 2021-02-16 19:50:46 +01:00
Max Kellermann
aa2e1bb310 encoder/wave: refactor fill_wave_header() to function 2021-02-16 19:50:43 +01:00
Max Kellermann
6153fca4fc tag/RiffFormat: add struct RiffFmtChunk 2021-02-16 19:50:40 +01:00
Max Kellermann
f090af0a22 tag/RiffFormat: add static_asserts on sizeof() 2021-02-16 19:50:25 +01:00
Max Kellermann
58f420fdca tag/RiffFormat: use CamelCase 2021-02-16 19:24:51 +01:00
Max Kellermann
ded2b31fbc tag/Riff: split into RiffId3.?xx and RiffFormat.hxx 2021-02-16 19:23:01 +01:00
Max Kellermann
75c8d2235b util/ByteOrder: add classes PackedBE16, PackedLE16, PackedLE32 2021-02-16 17:37:25 +01:00
Max Kellermann
f679961564 Merge tag 'v0.22.6'
release v0.22.6
2021-02-16 13:58:47 +01:00
Max Kellermann
471c37be59 queue/PlaylistEdit: convert start/end parameters to RangeArg 2021-02-15 22:51:09 +01:00
Max Kellermann
157ddcbab1 protocol/RangeArg: add methods ClipRelaxed(), CheckClip(), CheckAdjustEnd() 2021-02-15 22:50:49 +01:00
Max Kellermann
ab160aa359 queue/PlaylistEdit: check "current>=0" before updating it 2021-02-15 22:50:37 +01:00
Max Kellermann
ecc07e4e98 Merge tag 'v0.22.5'
release v0.22.5
2021-02-15 22:50:16 +01:00
Naglis Jonaitis
cbc830fd65 doc/client.rst: fix typo 2021-02-15 20:23:32 +02:00
Max Kellermann
98a9f81d61 doc/client.rst: new chapter about client development 2021-02-15 13:26:25 +01:00
AndriiZ
f5460b35a3 Add cacert option for Curl plugin. Allows to set cacert for curl lib ()
Add cacert option for curl plugin

    add cacert option for Curl plugin. Allows to set cacert for curl lib
    Added documentation line into doc/plugins.rst with explanation for cacert option
2021-02-13 13:49:15 +02:00
Max Kellermann
3456b1e50d use std::size_t 2021-02-12 18:39:28 +01:00
Max Kellermann
fe6abe1750 zlib/Error: add noexcept 2021-02-12 18:37:32 +01:00
Max Kellermann
6cdb3ff21e use [[gnu::pure]] instead of gcc_pure
This is semi-standard and doesn't require the util/Compiler.h header.
2021-02-12 18:34:16 +01:00
Max Kellermann
01af2778ab time/ISO8601: throw std::invalid_argument on error
Throwing std::runtime_error was slightly wrong.
2021-02-12 18:31:56 +01:00
Max Kellermann
ad03c70753 event/TimerWheel: workaround for GCC9 bug 2021-02-12 18:29:02 +01:00
Max Kellermann
7fe0095fa7 util/IntrusiveList: add noexcept to defaulted constructor 2021-02-11 21:22:15 +01:00
Max Kellermann
a4b236348f Merge branch 'v0.22.x' 2021-02-07 22:04:07 +01:00
Max Kellermann
aa40aae5bd doc/user.rst: add section about profiling MPD 2021-02-07 21:38:24 +01:00
Max Kellermann
5a16e3ffa3 event/TimerWheel: optimized container for CoarseTimerEvent 2021-02-05 18:24:25 +01:00
Max Kellermann
d1957b83c8 event/Chrono: add type alias TimePoint 2021-02-05 18:19:23 +01:00
Max Kellermann
1b4fd74575 event/TimerEvent: rename to FineTimerEvent
... and make TimerEvent a type alias for FineTimerEvent (i.e. swap
names).
2021-02-05 18:18:05 +01:00
Max Kellermann
def962b6cb event/{Coarse,Fine,Far}TimerEvent: aliases for TimerEvent
Preparing for a variant of TimerEvent with coarse 1-second
granularity, but cheaper (with a timer wheel).
2021-02-05 18:16:05 +01:00
Max Kellermann
e802f1f61a event/Loop: move TimerSet to separate class 2021-02-05 18:09:21 +01:00
Max Kellermann
271b287356 event/TimerEvent: assign due in Schedule() 2021-02-05 17:57:05 +01:00
Max Kellermann
2a30acd99c event/Loop: use [[gnu::pure]] 2021-02-05 17:55:33 +01:00
Max Kellermann
a8e70f18fd event/*: use using instead of typedef 2021-02-05 17:54:16 +01:00
Max Kellermann
ddd9f20a0b fs/io/GunzipReader: document that the constructor throws 2021-02-04 17:29:00 +01:00
Max Kellermann
f4a5d671fe util/{Const,Writable}Buffer: include cleanup 2021-02-04 17:27:48 +01:00
Richard Backhouse
c72006dbcc zeroconf/ZeroconfBonjour: Cleanup formatting from previous commit 2021-01-23 06:13:05 -05:00
Richard Backhouse
06fe30e2bd zeroconf/ZeroconfBonjour: Fix compile errors resulting from "refactor to SocketEvent" and reenable bonjour for darwin build 2021-01-23 06:06:49 -05:00
Max Kellermann
08e76815ba io/FileDescriptor: use std::byte 2021-01-22 12:50:16 +01:00
Max Kellermann
33ac3eb551 lib/curl/Easy: add methods SetVerify{Host,Peer}() 2021-01-22 12:34:04 +01:00
Max Kellermann
d56a51cb5e Merge branch 'v0.22.x' 2021-01-21 22:28:11 +01:00
Max Kellermann
065a0c09f8 fs/io/StdioOutputStream: include cleanup 2021-01-21 21:13:40 +01:00
Max Kellermann
04731fb7cc util/StringPointer: add operator==(std::nullptr_t) 2021-01-21 21:08:52 +01:00
Max Kellermann
12ff5a547f fs/io/FileOutputStream: add noexcept 2021-01-21 21:04:19 +01:00
Max Kellermann
9b2eb74f95 util/AllocatedString: fix operator= parameter type 2021-01-21 20:44:07 +01:00
Max Kellermann
84084baa65 util/AllocatedString: remove wrong std:: prefix 2021-01-21 20:16:32 +01:00
Max Kellermann
3bc45fbf68 util/AllocatedString: remove Null(), IsNull() 2021-01-21 20:12:05 +01:00
Max Kellermann
36168a24f5 util/AllocatedString: support casting a nulled instance to string_view 2021-01-21 20:06:01 +01:00
Max Kellermann
5e67443a1a util/{Const,Writable}Buffer: always enable assertions 2021-01-21 20:04:00 +01:00
Max Kellermann
17858143b3 util/{Const,Writable}Buffer: enable constexpr on more methods 2021-01-21 20:03:53 +01:00
Max Kellermann
c44a7b2705 util/AllocatedArray: add operator=(nullptr) 2021-01-21 20:03:38 +01:00
Max Kellermann
0ded23591b util/AllocatedString: add operator=() 2021-01-21 20:02:52 +01:00
Max Kellermann
c1a7aa652d util/AllocatedString: replace Clone() with copy constructor 2021-01-21 20:02:41 +01:00
Max Kellermann
8d47f51399 util/AllocatedString: add const_pointer constructor 2021-01-21 18:06:10 +01:00
Max Kellermann
a81c9bfb81 util/AllocatedString: add string_view constructor
Replaces the static Duplicate() method.
2021-01-21 18:05:51 +01:00
Max Kellermann
1caf57644f util/AllocatedString: add default constructor 2021-01-21 18:05:50 +01:00
Max Kellermann
c70b63c183 util/AllocatedString: rename to BasicAllocatedString
To make things simpler, AllocatedString is now a non-template class.
2021-01-21 18:04:03 +01:00
Max Kellermann
1b89b4ef83 Merge branch 'v0.22.x' 2021-01-21 17:45:15 +01:00
Max Kellermann
8279cafd6d Merge tag 'v0.22.4'
release v0.22.4
2021-01-21 17:42:26 +01:00
Max Kellermann
014c2a82bd event/SignalMonitor: fix non-Linux build failure
Regression by commit cd4b673b6c

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1060
2021-01-21 14:05:23 +01:00
Rosen Penev
594dfe572b clang-tidy: mark a bunch of variables constexpr
Found with cppcoreguidelines-interfaces-global-init

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-01-16 18:35:31 -08:00
Max Kellermann
906e82f600 event/Loop: fix assertion failure due to wrong "busy" value
If an InjectEvent callback schedules a timer, the loop will restart,
but the "busy" flag is still false.  The fix is to move the "again"
check before the "busy" setting.
2021-01-12 17:28:42 +01:00
Max Kellermann
bcb7e954e9 net/Resolver: add simple getaddrinfo() wrapper 2021-01-12 15:43:26 +01:00
Max Kellermann
866c87c65e net/ToString: include cleanup 2021-01-12 12:22:44 +01:00
Max Kellermann
4ba36d7cb9 net/SocketError: relicense to BSD-2 2021-01-11 22:31:46 +01:00
Max Kellermann
13f8a912e3 event/Loop: simplify wake_event initializer 2021-01-11 20:14:20 +01:00
Max Kellermann
51f110a990 system/EventPipe: use class Unique{Socket,File}Descriptor 2021-01-11 17:51:22 +01:00
Max Kellermann
17eae74c1c system/EventFD: Get() returns FileDescriptor 2021-01-11 17:38:30 +01:00
Max Kellermann
cd4b673b6c event/WakeFD: convert macro to class with a GetSocket() method 2021-01-11 17:02:54 +01:00
Ethan Halsall
0d606c743b add additional opus encoder options 2021-01-09 22:04:00 -06:00
Max Kellermann
81ea749248 Merge branch 'v0.22.x' 2021-01-05 13:11:29 +01:00
Max Kellermann
e009ad1a72 thread/Id: relicense to BSD-2 2021-01-03 19:25:20 +01:00
Max Kellermann
abbd980671 Merge branch 'v0.22.x' 2021-01-01 19:59:15 +01:00
Max Kellermann
937da63ba6 util/StringView: add {Starts,Ends}With(char) 2020-12-15 17:04:39 +01:00
Max Kellermann
1f312b2e42 curl/Handler: disallow OnData() to throw
This eliminates some complexity from class CurlRequest.
2020-12-15 17:03:24 +01:00
Max Kellermann
1e3089ffb7 curl/Request: move struct Pause to class CurlResponseHandler 2020-12-15 17:01:18 +01:00
Max Kellermann
5d7ff150dd curl/Request: add more wrapper methods 2020-12-15 16:56:20 +01:00
Max Kellermann
c767501c12 Merge branch 'v0.22.x' 2020-12-15 14:11:06 +01:00
EdJoPaTo
00602d28a4 doc/protocol.rst: ensure all commands have targets 2020-12-15 13:49:57 +01:00
Max Kellermann
7a56837141 odbus/Watch: support DBUS_WATCH_{ERROR,HANGUP} 2020-12-14 15:10:19 +01:00
Max Kellermann
ed1caffc79 odbus/Watch: use SocketEvent::ReleaseSocket() to allow another Open() 2020-12-14 15:10:15 +01:00
Max Kellermann
65473b5113 lib/dbus/FilterHelper: new class 2020-12-14 15:07:12 +01:00
Max Kellermann
178d115ccb lib/dbus/Glue: add noexcept 2020-12-14 13:19:51 +01:00
Max Kellermann
10e5b0759c lib/dbus/Glue: relicense to BSD-2 2020-12-14 13:02:37 +01:00
arcnmx
0a81e462db event/SocketEvent: remove FD before closing socket
SocketEvent knows the FD is still open and is about to close it, so
it's unnecessary to rely on the kernel (via AbandonFD) to clean up the
epoll_wait list.

### Why this is relevant

- `AbandonFD` assumes that upon closing the socket, the FD will be automatically removed from the epoll list. That fd is associated with a reference to the `SocketEvent`, so this is an important and dangerous assumption to get wrong. In the case that the FD isn't immediately removed from the list by the kernel, the event loop can crash due to the `SocketEvent` being destroyed and it being a use-after-free bug at that point.
- If a socket FD happens to be duplicated, then closing the SocketEvent FD will not automatically remove it from epoll, and will trigger said bug/crash. It is only automatically removed when all FD references to the underlying socket/resource are closed?
- A `fork()` is one example where a socket FD can be duplicated and result in this situation.
    - `CLOEXEC` might be considered mitigation for this but also introduces a race condition where the crash can occur between a `fork()` and `exec()` without additional synchronization to freeze the event loop.

One could argue the mpd event loop isn't fork-safe, and thus should be allowed to use `AbandonFD` however it likes. A decision on whether this is intended should probably be declared; but either way this fix seems appropriate in cases where `Abandon` isn't actually necessary. It also might be possible to fix `AbandonFD` to mark the `SocketEvent` as removed without using `EPOLL_CTL_DEL`?

[edit: made this dependent on HAVE_THREADED_EVENT_LOOP which is always
true for MPD, but not for ncmpc, for example - mk]
2020-12-04 10:32:46 +01:00
Max Kellermann
5cbbe8ae2e event/TimerEvent: update API documentation 2020-12-04 09:57:19 +01:00
Max Kellermann
00fafa16c7 event/SocketEvent: remove assert(), reduce header dependencies 2020-12-04 09:56:28 +01:00
Max Kellermann
cea8db7eaa event/SocketEvent: add comment 2020-12-04 09:55:08 +01:00
Max Kellermann
b56c0e69e4 event/SocketEvent: add another assert() to Open() 2020-12-04 09:24:02 +01:00
Max Kellermann
b27e82e4a9 event/SocketEvent: allow Schedule() with IMPLICIT_FLAGS
Relax the API (instead of tightening it further like commit
7bc1c9925b tried to do unsuccessfully).
2020-12-04 09:17:45 +01:00
Max Kellermann
ad48834469 Revert "event/SocketEvent: add assert()"
This reverts commit 7bc1c9925b.  It
caused a crash with the ALSA plugin family (through
MultiSocketMonitor::ReplaceSocketList() and
MultiSocketMonitor::AddSocket()).  Until we have a proper fix, the
assertion patch is reverted.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1020
2020-12-02 20:43:29 +01:00
Max Kellermann
9d6b5e2ba1 event/TimerEvent: rename IsActive() to IsPending() 2020-12-02 15:41:11 +01:00
Max Kellermann
33ba190bec event/TimerEvent: add ScheduleEarlier() 2020-12-02 15:30:37 +01:00
Max Kellermann
3783350d25 event/SocketEvent: document Dispatch() 2020-12-02 15:24:23 +01:00
Max Kellermann
173405a343 event/SocketEvent: make Dispatch() private 2020-12-02 15:14:51 +01:00
Max Kellermann
7bc1c9925b event/SocketEvent: add assert() 2020-12-02 15:12:30 +01:00
Max Kellermann
ce4c69dd95 test/util/TestException: new unit test 2020-12-02 15:09:47 +01:00
Max Kellermann
8eea825462 test/util/TestTemplateString: new unit test 2020-12-02 15:03:51 +01:00
Max Kellermann
49e1ce7c43 test/TestUtil: move to test/util/ 2020-12-02 15:02:45 +01:00
Max Kellermann
618f94f589 util/TemplateString: add StringView cast operator 2020-12-02 15:00:49 +01:00
Max Kellermann
ad2c22844c util/TemplateString: add FromLiteral() 2020-12-02 15:00:39 +01:00
Max Kellermann
b8df851414 io/FileDescriptor: use std::size_t 2020-12-02 14:56:00 +01:00
Max Kellermann
a584141cae io/Open: add flags parameter to OpenReadOnly() 2020-12-02 14:52:51 +01:00
Max Kellermann
4e88f95f94 event/Loop: move the "again" check out of the mutex scope 2020-12-01 20:29:21 +01:00
Max Kellermann
790e540c19 event/Loop: use ClockCache 2020-12-01 20:25:42 +01:00
Max Kellermann
16074c565f time/ClockCache: new library 2020-12-01 20:22:26 +01:00
Max Kellermann
2a1dd55b11 event/Loop: include cleanup 2020-12-01 20:19:40 +01:00
Max Kellermann
be20f760ab event/Loop: disallow copying 2020-12-01 20:18:33 +01:00
Max Kellermann
8050394003 event/Loop: add noexcept 2020-12-01 20:10:53 +01:00
Max Kellermann
ff8b5bc61b event/Loop: reorder methods 2020-12-01 20:09:25 +01:00
Max Kellermann
ef8797821f event/Loop: inline field initializers 2020-12-01 20:07:35 +01:00
Max Kellermann
5f2797e7cc event/Loop: add more assertions to dtor 2020-12-01 20:05:54 +01:00
Max Kellermann
e286702f4c event/Loop: rename AddDeferred() to AddDefer() 2020-12-01 17:26:39 +01:00
Max Kellermann
c58aaf545f event/IdleEvent: make a special case of DeferEvent 2020-12-01 17:14:24 +01:00
Max Kellermann
990f2dc1cf event/DeferEvent: use class IntrusiveList instead of boost::intrusive::list 2020-12-01 17:14:24 +01:00
Max Kellermann
774b4313f2 event/DeferEvent: split the thread-safe version into new class InjectEvent 2020-12-01 17:14:24 +01:00
Max Kellermann
1ecbc2ff0f event/DeferEvent: explicitly forbid copying 2020-12-01 17:14:24 +01:00
Max Kellermann
fd8e38f8cd event/DeferEvent: use using instead of typedef 2020-12-01 17:14:24 +01:00
Max Kellermann
e86d4db55c Merge branch 'v0.22.x' 2020-12-01 17:14:21 +01:00
Max Kellermann
9420c74101 util/AllocatedArray: add nullptr constructor 2020-11-30 22:30:57 +01:00
Max Kellermann
b1bef9c21d util/AllocatedArray: add method data() 2020-11-30 22:30:28 +01:00
Max Kellermann
5b0ef7ea98 util/AllocatedArray: add types pointer, const_pointer 2020-11-30 22:30:25 +01:00
Max Kellermann
ab53c414bc util/StringView: add method SplitLast() 2020-11-30 22:29:38 +01:00
Max Kellermann
d547ace749 io/FileDescriptor: use std::size_t 2020-11-30 22:27:16 +01:00
Max Kellermann
b47e0cffdd util/TemplateString: rename CharAsString() to FromChar() 2020-11-30 22:23:28 +01:00
Max Kellermann
3af35aee9e util/TemplateString: add cast operators 2020-11-30 22:22:24 +01:00
Max Kellermann
02314ac7dd util/TemplateString: rewrite as constexpr functions
Hooray C++17!
2020-11-30 22:19:57 +01:00
Max Kellermann
e7c4e87ac4 util/TemplateString: remove unnecessary implementation for static variable
This is obsolete since C++17.
2020-11-30 22:13:13 +01:00
Max Kellermann
de58bfbb7f util/TemplateString: use std::size_t 2020-11-30 22:13:08 +01:00
Max Kellermann
0dda4c06b1 util/TemplateString: no indent after namespace 2020-11-30 22:13:04 +01:00
Max Kellermann
79fd6143ec lib/dbus/Values: use T::Traits, not T
This template was never instantiated, so the problem never occurred.
2020-11-30 22:11:18 +01:00
Max Kellermann
8f89e3f7f4 lib/dbus/Values: use using instead of typedef 2020-11-30 22:11:14 +01:00
Max Kellermann
fc01d11b8d odbus/Types: use using instead of typedef 2020-11-30 22:08:37 +01:00
Max Kellermann
0c28d8dcbe time/ISO8601: support YYYY-MM (without day of month) 2020-11-30 21:55:12 +01:00
Max Kellermann
764eaadd25 time/Math: new library 2020-11-30 21:55:05 +01:00
Max Kellermann
273771ffec net/SocketAddress: add CastTo() 2020-11-30 21:51:07 +01:00
Max Kellermann
32ce9ce919 net/IPv[46]Address: pass SocketAddress by value to Cast() 2020-11-30 21:49:07 +01:00
Max Kellermann
34a070f5a6 net/IPv[46]Address: add Cast(const sockaddr_in&) 2020-11-30 21:49:02 +01:00
Max Kellermann
ac4975cd7a util/MimeType: relicense to BSD-2 2020-11-18 15:28:21 +01:00
Max Kellermann
fbbbfb9668 Merge branch 'v0.22.x' 2020-11-16 09:41:20 +01:00
Max Kellermann
eb9f5339b6 Merge branch 'v0.22.x' into master 2020-11-11 12:43:50 +01:00
Max Kellermann
a9714e73c8 Merge branch 'bind' of git://github.com/neheb/MPD into master 2020-11-10 16:02:34 +01:00
Max Kellermann
a99bc91eb0 Merge tag 'v0.22.3' into master
release v0.22.3
2020-11-06 16:14:46 +01:00
Rosen Penev
071d3c71d8 clang-tidy: replace std::bind with lambdas
Found with modernize-avoid-bind

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2020-11-05 18:09:30 -08:00
Max Kellermann
afbcac9fb1 util/MimeType: use IterableSplitString() in ParseMimeTypeParameters() 2020-11-04 21:29:42 +01:00
Max Kellermann
51e5b56b3a playlist/registry: remove duplicate function ExtractMimeTypeMainPart()
This is the same as GetMimeTypeBase(), which has already been applied.
2020-11-04 21:20:43 +01:00
Max Kellermann
bb07fd42ce util/MimeType: migrate GetMimeTypeBase() to std::string_view 2020-11-04 21:20:03 +01:00
Max Kellermann
bab626c325 util/UriExtract: remove the query string at the beginning of uri_get_suffix() 2020-11-04 21:15:41 +01:00
Max Kellermann
2a9131498f util/UriExtract: pass std::string_view to uri_get_suffix() 2020-11-04 21:13:57 +01:00
Max Kellermann
35a232105e util/UriExtract: uri_get_suffix() returns std::string_view
No need to copy it to a buffer.
2020-11-04 21:08:26 +01:00
Max Kellermann
19dd1a25d7 {decoder,archive,playlist}/plugin: pass std::string_view to SupportsMimeType() 2020-11-04 21:00:49 +01:00
Max Kellermann
53396c0e50 Merge branch 'v0.22.x' into master 2020-11-04 20:37:25 +01:00
Max Kellermann
0b8208fe7f Merge branch 'clng11' of git://github.com/neheb/MPD into master 2020-11-04 20:34:55 +01:00
Max Kellermann
3d276d50b4 event/PollBackend: use vector::push_back() instead of resize() 2020-10-30 16:35:20 +01:00
Max Kellermann
b1b731340e event/PollBackend: add Item constructor 2020-10-30 16:32:45 +01:00
Max Kellermann
b9b02b4ff2 event/PollBackend: use unordered_map::find() instead of operator[]
The latter creates a new object, but we know that the key already
exists.
2020-10-30 16:25:41 +01:00
Max Kellermann
ab5d23da11 event/PollBackend: use unordered_map::emplace() in Add() 2020-10-30 16:24:32 +01:00
Max Kellermann
0554fe3652 event/PollBackend: use std::size_t 2020-10-30 16:09:29 +01:00
Max Kellermann
b0282fe36f event/PollGroupWinSelect: add Item constructor 2020-10-30 16:07:23 +01:00
Max Kellermann
69b45e693b event/WinSelect: use unordered_map::find() instead of operator[]
The latter creates a new object, but we know that the key already
exists.
2020-10-30 16:05:25 +01:00
Max Kellermann
9e97acc28d event/WinSelect: merge duplicate code into ApplyReady() 2020-10-30 15:55:23 +01:00
Max Kellermann
b1e446a931 event/WinSelect: add missing const to deleted copy ctor/operator 2020-10-30 15:45:29 +01:00
Max Kellermann
938319cd44 event/WinSelect: reorder method prototypes 2020-10-30 15:45:12 +01:00
Max Kellermann
fee29001fa event/WinSelect: use unordered_map::emplace() in Add()
This allow using erase() with iterator, without a key lookup.
2020-10-30 15:32:11 +01:00
Max Kellermann
6d894a1806 event/WinSelect: use SOCKET as std::unordered_map key 2020-10-30 15:25:09 +01:00
Rosen Penev
f1fc5d79ca clang-tidy: convert to all/any_of
Found with readability-use-anyofallof

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2020-10-28 15:51:21 -07:00
Rosen Penev
0fd2c74a66 use structured binding declarations
Shorter.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2020-10-28 15:41:31 -07:00
Max Kellermann
bb99cf37e3 Merge tag 'v0.22.2' into master
release v0.22.2
2020-10-28 17:33:10 +01:00
Max Kellermann
7c47fe746c event/Loop: AbandonFD() unlinks the SocketEvent
Fixes use-after-free bugs causing assertion failures at shutdown,
because all "abandoned" SocketEvents are still in the linked list.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/986
Closes https://github.com/MusicPlayerDaemon/MPD/issues/987
2020-10-28 15:01:32 +01:00
Max Kellermann
65a1c4a016 event/Loop: pass SocketEvent& to AbandonFD() 2020-10-28 14:59:28 +01:00
Max Kellermann
46418d0f2d event/ServerSocket: remove obsolete API documentation 2020-10-28 14:52:31 +01:00
arcnmx
ad585e179f system/FileDescriptor: fix Duplicate result
dup2 returns new_fd on success, not 0
2020-10-20 09:32:43 -07:00
Max Kellermann
8348a1ec8f event/PollGroup: rename to PollBackend 2020-10-19 14:52:59 +02:00
Max Kellermann
c18e00daa4 event/PollGroup: move event flags to a separate header
Reduce header dependencies for SocketEvent.hxx.
2020-10-19 14:48:41 +02:00
Max Kellermann
418ba96334 event/SocketEvent: forbid copying 2020-10-18 20:07:49 +02:00
Max Kellermann
a60e782959 event/Loop: reorder assertions 2020-10-18 20:05:22 +02:00
Max Kellermann
8bab5733d7 event/Loop: add assertions 2020-10-18 20:04:16 +02:00
Max Kellermann
e3270dfd68 event/SocketEvent: use class IntrusiveList<> 2020-10-18 20:02:47 +02:00
Max Kellermann
a14997ffb8 event/Loop: manage all SocketEvents in a linked list
Not only those which are "ready".
2020-10-18 20:01:38 +02:00
Max Kellermann
dd94f97572 event/Loop: un-inline AddFD(), ModifyFD()
Prepare for adding more code here.
2020-10-18 19:58:42 +02:00
Max Kellermann
7d502fb448 event/Loop: round epoll_wait() timeout up
This implements proper rounding, amending commit dcbb9fe07c
2020-10-18 19:58:42 +02:00
Max Kellermann
3ac87bbcda io/uring/Queue: use IntrusiveList<> 2020-10-18 19:37:54 +02:00
Max Kellermann
f64799622d event/IdleEvent: use class IntrusiveList<> 2020-10-18 19:28:12 +02:00
Max Kellermann
6f0ad2b6c5 util/IntrusiveList: replacement for boost::intrusive::list 2020-10-18 19:23:34 +02:00
Max Kellermann
b5750afb24 event/IdleEvent: use auto 2020-10-18 19:23:34 +02:00
Max Kellermann
442dd5e955 event/IdleEvent: forbid copying 2020-10-18 19:23:25 +02:00
Max Kellermann
172c2ae1aa Merge tag 'v0.22.1' into master
release v0.22.1
2020-10-17 13:58:36 +02:00
Max Kellermann
4f0e0af319 Merge branch 'v0.22.x' into master 2020-10-16 19:02:03 +02:00
Max Kellermann
cb382b1e7d event/PollGroupWinSelect: add missing return value
Fixes regression from commit 1473d8474f
2020-10-16 19:02:00 +02:00
Max Kellermann
871bf3b88f command: add command "getvol"
Closes https://github.com/MusicPlayerDaemon/MPD/issues/979
2020-10-16 17:51:51 +02:00
Max Kellermann
08360e401d meson.build: increment protocol version to 0.23 2020-10-16 17:46:47 +02:00
Max Kellermann
b611b1824a event/Loop: move code to Wait() 2020-10-15 20:21:00 +02:00
Max Kellermann
1473d8474f event/PollGroup: ReadEvents() returns PollResult 2020-10-15 20:15:09 +02:00
Max Kellermann
0ecc3394c3 Merge branch 'v0.22.x' into master 2020-10-15 20:14:36 +02:00
Max Kellermann
725985379a event/SocketEvent: add ScheduleImplicit(), Is{Read,Write}Pending() 2020-10-15 17:01:30 +02:00
Max Kellermann
8849b9b62c event/SocketEvent: move Abandon() up 2020-10-15 16:59:45 +02:00
Max Kellermann
caa2611ad5 event/SocketEvent: add Abandon() 2020-10-15 16:57:55 +02:00
Max Kellermann
f8ff597963 event/SocketEvent: document Close() 2020-10-15 16:55:31 +02:00
Max Kellermann
ff6e434caf event/SocketEvent: rename Steal() to ReleaseSocket() 2020-10-15 16:54:33 +02:00
Max Kellermann
95bb12880d net/UniqueSocketDescriptor: add noexcept 2020-10-15 16:46:29 +02:00
Max Kellermann
257196664a net/SocketAddress: add missing include 2020-10-15 16:45:50 +02:00
Max Kellermann
643bf95366 util/OffsetPointer: use std::ptrdiff_t 2020-10-15 16:41:39 +02:00
Max Kellermann
36a187da39 util/Cast: include cleanup 2020-10-15 16:40:26 +02:00
Max Kellermann
fec80f2835 util/Cast: use std::ptrdiff_t 2020-10-15 16:40:18 +02:00
Max Kellermann
4e47653cf6 lib/curl/Global: ignore curl_multi_socket_action() errors
Keep the logging library out of this low-level libcurl wrapper.
2020-10-15 16:31:31 +02:00
Max Kellermann
c13004f985 lib/curl/Multi: add Add(), Remove(), ... 2020-10-15 16:25:57 +02:00
Max Kellermann
baba41e304 lib/curl/Global: adjust parameter name 2020-10-15 16:16:02 +02:00
Max Kellermann
d87e09a8b4 lib/dbus/ReadIter: remove obsolete GCC warning workaround
We need at least GCC 8 anyway.
2020-10-15 16:08:29 +02:00
Max Kellermann
33146ac353 lib/dbus/Watch: adjust parameter name 2020-10-15 16:06:19 +02:00
Max Kellermann
bb20af8f20 util/StringStrip: use std::strlen() 2020-10-15 12:54:47 +02:00
Max Kellermann
9355ec44e0 util/StringStrip: use std::size_t 2020-10-15 12:53:02 +02:00
Max Kellermann
c63bd323ce util/StringCompare: use std::memcmp() 2020-10-15 12:48:33 +02:00
Max Kellermann
55db7105c5 event/SocketEvent: check/clear scheduled_flags in Close()
Fixes regression by commit 521e573be9
2020-10-14 21:08:39 +02:00
Max Kellermann
1c079e554b io/UniqueFileDescriptor: add method Release() 2020-10-14 20:50:02 +02:00
Max Kellermann
48afb68f3a event/SocketEvent: remove assert() from GetScheduledFlags()
Fixes regression by commit 7901b04c78
2020-10-14 20:50:02 +02:00
Max Kellermann
21f409d5e2 event/SocketEvent: fix grammar 2020-10-14 16:30:38 +02:00
Max Kellermann
521e573be9 event/SocketEvent: use EventLoop::AbandonFD() in Close() 2020-10-14 16:29:49 +02:00
Max Kellermann
abf9ae2dd9 event/Loop: rename Abandon() to AbandonFD() 2020-10-14 16:26:06 +02:00
Max Kellermann
9f013f7de4 event/SocketEvent: allow Close() without socket 2020-10-14 16:26:01 +02:00
Max Kellermann
7fc04fd5cd event/SocketEvent: move Dispatch() down 2020-10-14 16:21:41 +02:00
Max Kellermann
7901b04c78 event/SocketEvent: allow Cancel() without socket 2020-10-14 16:20:43 +02:00
Max Kellermann
653eea5840 event/SocketEvent: remove unnecessary initializer 2020-10-14 16:18:39 +02:00
Max Kellermann
5a4055fb08 event/SocketMonitor: refactor to SocketEvent
Similar to commits 1686f4e857 and
30a5dd267b
2020-10-14 15:54:12 +02:00
Max Kellermann
4d68a12f03 event/Loop: split the AtScopeExit()
Fixes the !HAVE_THREADED_EVENT_LOOP build.
2020-10-14 14:47:59 +02:00
Max Kellermann
0e951da64b event/Loop: add missing #ifdef 2020-10-14 14:37:29 +02:00
Max Kellermann
38dab040b3 event/Loop: add compile-time option to disable multithreading
Not for MPD, but for other applications which might want to copy its
event loop, but do not need multi-threading.
2020-10-14 14:08:38 +02:00
Max Kellermann
e9f6af61f9 event/Loop: forward-declare class {Idle,Defer}Event 2020-10-14 14:05:17 +02:00
Max Kellermann
b06c4e2711 event/{Idle,Defer}Event: use base_hook instead of member_hook
Allows forward declaration.
2020-10-14 14:04:30 +02:00
Max Kellermann
1686f4e857 event/IdleMonitor: refactor to IdleEvent
Instead of using this as a base class implementing a virtual method,
the new class IdleEvent can be used as a variable, decoupling
IdleMonitor's internal state from the derived class.

This is similar to commit 30a5dd267b
which refactored TimeoutMonitor to TimerEvent.
2020-10-14 13:47:25 +02:00
Max Kellermann
9f57732af2 meson.build: increment version to 0.23~git
The new stable branch v0.22.x was forked off and is feature-frozen, so
the "master" branch will become the next major version eventually.
2020-10-14 12:13:12 +02:00
Max Kellermann
329382c1da event/SignalMonitor: add noexcept 2020-10-13 17:26:33 +02:00
Max Kellermann
fadc03df21 meson.build: move macros to event/Features.h 2020-10-13 16:15:52 +02:00
Max Kellermann
54ee0e28ab event/PollGroup: check _WIN32 instead of USE_WINSELECT 2020-10-13 15:27:58 +02:00
Max Kellermann
92fc53ebef event/PollGroupWinSelect: use range-based for 2020-10-12 12:44:23 +02:00
Max Kellermann
7e7a1613cf event/PollGroupWinSelect: use std::copy_n() 2020-10-12 12:40:32 +02:00
Max Kellermann
f73c4643ef event/PollGroupWinSelect: move IsEmpty() check to GetPtr() 2020-10-12 12:39:10 +02:00
Max Kellermann
8b94e8f260 event/PollGroupWinSelect: use SOCKET instead of int 2020-10-12 12:13:46 +02:00
Max Kellermann
41bc17a27f event/SocketMonitor: add ready_flags
By making each SocketMonitor keep track of its ready flags, this
removes a lot of overhead from EventLoop::RemoveFD().
2020-10-09 17:28:11 +02:00
751 changed files with 15230 additions and 8703 deletions
.travis.ymlNEWSREADME.md
android
doc
meson.buildmeson_options.txt
python/build
src
CommandLine.cxxIdleFlags.cxxIdleFlags.hxxInstance.cxxInstance.hxxListen.cxxLocateUri.cxxLog.cxxLog.hxxLogBackend.cxxLogInit.cxxMain.cxxMain.hxxMapper.hxxMixRampInfo.hxxMusicBuffer.hxxMusicChunk.hxxMusicPipe.hxxPartition.cxxPartition.hxxPermission.cxxPermission.hxxPlaylistPrint.cxxPlaylistPrint.hxxRemoteTagCache.cxxRemoteTagCache.hxxReplayGainInfo.hxxReplayGainMode.cxxReplayGainMode.hxxSingleMode.cxxSingleMode.hxxSongLoader.hxxSongPrint.cxxSongSave.cxxSongSave.hxxSongUpdate.cxxStateFile.cxxStateFile.hxxStats.cxxTagAny.cxxTagPrint.cxxTagStream.cxxTimePrint.cxx
android
archive
client
command
config
db
decoder
encoder
event
filter
fs
input
io
java
lib
ls.cxxls.hxx
mixer
neighbor
net
output
pcm
player
playlist
protocol
queue
song
sticker
storage
system
tag
thread
time
unix
util
win32
zeroconf
subprojects
test
win32

@@ -11,6 +11,7 @@ jobs:
- meson
- libgtest-dev
- libboost-dev
- libfmt-dev
# Ubuntu Focal (20.04) with GCC 9.3 on big-endian
- os: linux
@@ -22,6 +23,7 @@ jobs:
- meson
- libgtest-dev
- libboost-dev
- libfmt-dev
# Ubuntu Focal (20.04) with GCC 9.3 on ARM64
- os: linux
@@ -33,6 +35,7 @@ jobs:
- meson
- libgtest-dev
- libboost-dev
- libfmt-dev
# Ubuntu Trusty (16.04) with GCC 8
- os: linux
@@ -67,6 +70,7 @@ jobs:
packages:
- ccache
- meson
- fmt
- googletest
- icu4c
- ffmpeg

86
NEWS

@@ -1,3 +1,89 @@
ver 0.23.1 (2021/10/19)
* protocol
- use decimal notation instead of scientific notation
- "load" supports relative positions
* output
- emit "mixer" idle event when replay gain changes volume
- pipewire: emit "mixer" idle events on external volume change
- pipewire: attempt to change the graph sample rate
- snapcast: fix time stamp bug which caused "Failed to get chunk"
* fix libfmt linker problems
* fix broken password authentication
ver 0.23 (2021/10/14)
* protocol
- new command "getvol"
- show the audio format in "playlistinfo"
- support "listfiles" with arbitrary storage plugins
- support relative positions in "addid"
- fix relative positions in "move" and "moveid"
- add "position" parameter to "findadd" and "searchadd"
- add position parameter to "load"
* database
- proxy: require MPD 0.20 or later
- proxy: require libmpdclient 2.11 or later
- proxy: split search into chunks to avoid exceeding the output buffer
- simple: add option to hide CUE target songs
- upnp: support libnpupnp instead of libupnp
* archive
- zzip, iso9660: ignore file names which are invalid UTF-8
* decoder
- openmpt: new plugin
- wavpack: fix WVC file support
* player
- do not cross-fade songs shorter than 20 seconds
* output
- oss: support DSD over PCM
- pipewire: new plugin
- snapcast: new plugin
* tags
- new tags "ComposerSort", "Ensemble", "Movement", "MovementNumber", and "Location"
* split permission "player" from "control"
* add option "host_permissions"
* new build-time dependency: libfmt
ver 0.22.11 (2021/08/24)
* protocol
- fix "albumart" crash
* filter
- ffmpeg: pass "channel_layout" instead of "channels" to buffersrc
- ffmpeg: fix "av_buffersink_get_frame() failed: Resource temporarily unavailable"
- ffmpeg: support double-precision samples (by converting to single precision)
* Android
- build with NDK r23
- playlist_directory defaults to "/sdcard/Android/data/org.musicpd/files/playlists"
ver 0.22.10 (2021/08/06)
* protocol
- support "albumart" for virtual tracks in CUE sheets
* database
- simple: fix crash bug
- simple: fix absolute paths in CUE "as_directory" entries
- simple: prune CUE entries from database for non-existent songs
* input
- curl: fix crash bug after stream with Icy metadata was closed by peer
- tidal: remove defunct unmaintained plugin
* tags
- fix crash caused by bug in TagBuilder and a few potential reference leaks
* output
- httpd: fix missing tag after seeking into a new song
- oss: fix channel order of multi-channel files
* mixer
- alsa: fix yet more rounding errors
ver 0.22.9 (2021/06/23)
* database
- simple: load all .mpdignore files of all parent directories
* tags
- fix "readcomments" and "readpicture" on remote files with ID3 tags
* decoder
- ffmpeg: support the tags "sort_album", "album-sort", "artist-sort"
- ffmpeg: fix build failure with FFmpeg 3.4
* Android
- fix auto-start on boot in Android 8 or later
* Windows
- fix build failure with SQLite
ver 0.22.8 (2021/05/22)
* fix crash bug in "albumart" command (0.22.7 regression)

@@ -14,7 +14,7 @@ For basic installation instructions
- [Manual](http://www.musicpd.org/doc/user/)
- [Forum](http://forum.musicpd.org/)
- [IRC](irc://chat.freenode.net/#mpd)
- [IRC](ircs://irc.libera.chat:6697/#mpd)
- [Bug tracker](https://github.com/MusicPlayerDaemon/MPD/issues/)
# Developers

@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd"
android:installLocation="auto"
android:versionCode="56"
android:versionName="0.22.8">
android:versionCode="61"
android:versionName="0.23.1">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
@@ -17,6 +17,8 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<application android:allowBackup="true"
android:requestLegacyExternalStorage="true"
@@ -41,6 +43,7 @@
<receiver android:name=".Receiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.HEADSET_PLUG" />
</intent-filter>
</receiver>
<service android:name=".Main" android:process=":main"/>

@@ -24,15 +24,13 @@ android_abis = {
'armeabi-v7a': {
'arch': 'arm-linux-androideabi',
'ndk_arch': 'arm',
'toolchain_arch': 'arm-linux-androideabi',
'llvm_triple': 'armv7-linux-androideabi',
'cflags': '-fpic -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=softfp',
'cflags': '-fpic -mfpu=neon -mfloat-abi=softfp',
},
'arm64-v8a': {
'arch': 'aarch64-linux-android',
'ndk_arch': 'arm64',
'toolchain_arch': 'aarch64-linux-android',
'llvm_triple': 'aarch64-linux-android',
'cflags': '-fpic',
},
@@ -40,7 +38,6 @@ android_abis = {
'x86': {
'arch': 'i686-linux-android',
'ndk_arch': 'x86',
'toolchain_arch': 'x86',
'llvm_triple': 'i686-linux-android',
'cflags': '-fPIC -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32',
},
@@ -48,7 +45,6 @@ android_abis = {
'x86_64': {
'arch': 'x86_64-linux-android',
'ndk_arch': 'x86_64',
'toolchain_arch': 'x86_64',
'llvm_triple': 'x86_64-linux-android',
'cflags': '-fPIC -m64',
},
@@ -84,36 +80,29 @@ class AndroidNdkToolchain:
ndk_arch = abi_info['ndk_arch']
android_api_level = '21'
# select the NDK compiler
gcc_version = '4.9'
install_prefix = os.path.join(arch_path, 'root')
self.arch = arch
self.actual_arch = arch
self.install_prefix = install_prefix
toolchain_path = os.path.join(ndk_path, 'toolchains', abi_info['toolchain_arch'] + '-' + gcc_version, 'prebuilt', build_arch)
llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch)
llvm_triple = abi_info['llvm_triple'] + android_api_level
common_flags = '-Os -g'
common_flags += ' ' + abi_info['cflags']
toolchain_bin = os.path.join(toolchain_path, 'bin')
llvm_bin = os.path.join(llvm_path, 'bin')
self.cc = os.path.join(llvm_bin, 'clang')
self.cxx = os.path.join(llvm_bin, 'clang++')
common_flags += ' -target ' + llvm_triple + ' -gcc-toolchain ' + toolchain_path
common_flags += ' -target ' + llvm_triple
common_flags += ' -fvisibility=hidden -fdata-sections -ffunction-sections'
# required flags from https://android.googlesource.com/platform/ndk/+/ndk-release-r20/docs/BuildSystemMaintainers.md#additional-required-arguments
common_flags += ' -fno-addrsig'
self.ar = os.path.join(toolchain_bin, arch + '-ar')
self.ranlib = os.path.join(toolchain_bin, arch + '-ranlib')
self.nm = os.path.join(toolchain_bin, arch + '-nm')
self.strip = os.path.join(toolchain_bin, arch + '-strip')
self.ar = os.path.join(llvm_bin, 'llvm-ar')
self.ranlib = os.path.join(llvm_bin, 'llvm-ranlib')
self.nm = os.path.join(llvm_bin, 'llvm-nm')
self.strip = os.path.join(llvm_bin, 'llvm-strip')
self.cflags = common_flags
self.cxxflags = common_flags
@@ -151,10 +140,7 @@ class AndroidNdkToolchain:
# default one on the build host
import shutil
bin_dir = os.path.join(install_prefix, 'bin')
try:
os.makedirs(bin_dir)
except:
pass
os.makedirs(bin_dir, exist_ok=True)
self.pkg_config = shutil.copy(os.path.join(mpd_path, 'build', 'pkg-config.sh'),
os.path.join(bin_dir, 'pkg-config'))
self.env['PKG_CONFIG'] = self.pkg_config
@@ -164,7 +150,6 @@ from build.libs import *
thirdparty_libs = [
libmpdclient,
libogg,
libvorbis,
opus,
flac,
libid3tag,
@@ -174,7 +159,6 @@ thirdparty_libs = [
ffmpeg,
openssl,
curl,
libexpat,
libnfs,
boost,
]

@@ -23,6 +23,12 @@
android:layout_height="wrap_content"
android:text="@string/checkbox_wakelock" />
<CheckBox
android:id="@+id/pause_on_headphones_disconnect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/checkbox_pause_on_headphones_disconnect" />
<TextView
android:id="@+id/status"
android:layout_width="wrap_content"

@@ -8,4 +8,5 @@
<string name="toggle_button_run_off">MPD is not running</string>
<string name="checkbox_run_on_boot">Run MPD automatically on boot</string>
<string name="checkbox_wakelock">Prevent suspend when MPD is running (Wakelock)</string>
<string name="checkbox_pause_on_headphones_disconnect">Pause MPD when headphones disconnect</string>
</resources>

@@ -33,4 +33,5 @@ public class Bridge {
public static native void run(Context context, LogListener logListener);
public static native void shutdown();
public static native void pause();
}

@@ -5,6 +5,7 @@ interface IMain
{
void start();
void stop();
void setPauseOnHeadphonesDisconnect(boolean enabled);
void setWakelockEnabled(boolean enabled);
boolean isRunning();
void registerCallback(IMainCallback cb);

@@ -24,9 +24,13 @@ import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothClass;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Build;
import android.os.IBinder;
@@ -55,6 +59,7 @@ public class Main extends Service implements Runnable {
private String mError = null;
private final RemoteCallbackList<IMainCallback> mCallbacks = new RemoteCallbackList<IMainCallback>();
private final IBinder mBinder = new MainStub(this);
private boolean mPauseOnHeadphonesDisconnect = false;
private PowerManager.WakeLock mWakelock = null;
static class MainStub extends IMain.Stub {
@@ -68,6 +73,9 @@ public class Main extends Service implements Runnable {
public void stop() {
mService.stop();
}
public void setPauseOnHeadphonesDisconnect(boolean enabled) {
mService.setPauseOnHeadphonesDisconnect(enabled);
}
public void setWakelockEnabled(boolean enabled) {
mService.setWakelockEnabled(enabled);
}
@@ -191,6 +199,28 @@ public class Main extends Service implements Runnable {
if (mThread != null)
return;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_HEADSET_PLUG);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (!mPauseOnHeadphonesDisconnect) {
return;
}
if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
if (intent.hasExtra("state") && intent.getIntExtra("state", 0) == 0)
pause();
} else {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBluetoothClass().hasService(BluetoothClass.Service.AUDIO))
pause();
}
}
}, filter);
final Intent mainIntent = new Intent(this, Settings.class);
mainIntent.setAction("android.intent.action.MAIN");
mainIntent.addCategory("android.intent.category.LAUNCHER");
@@ -241,6 +271,21 @@ public class Main extends Service implements Runnable {
stopSelf();
}
private void pause() {
if (mThread != null) {
if (mThread.isAlive()) {
synchronized (this) {
if (mStatus == MAIN_STATUS_STARTED)
Bridge.pause();
}
}
}
}
private void setPauseOnHeadphonesDisconnect(boolean enabled) {
mPauseOnHeadphonesDisconnect = enabled;
}
private void setWakelockEnabled(boolean enabled) {
if (enabled && mWakelock == null) {
PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
@@ -368,6 +413,19 @@ public class Main extends Service implements Runnable {
}
}
public boolean setPauseOnHeadphonesDisconnect(boolean enabled) {
synchronized (this) {
if (mIMain != null) {
try {
mIMain.setPauseOnHeadphonesDisconnect(enabled);
return true;
} catch (RemoteException e) {
}
}
return false;
}
}
public boolean setWakelockEnabled(boolean enabled) {
synchronized (this) {
if (mIMain != null) {
@@ -414,6 +472,15 @@ public class Main extends Service implements Runnable {
* start Main service without any callback
*/
public static void start(Context context, boolean wakelock) {
context.startService(new Intent(context, Main.class).putExtra("wakelock", wakelock));
Intent intent = new Intent(context, Main.class)
.putExtra("wakelock", wakelock);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
/* in Android 8+, we need to use this method
or else we'll get "IllegalStateException:
app is in background" */
context.startForegroundService(intent);
else
context.startService(intent);
}
}

@@ -59,6 +59,7 @@ public class Settings extends Activity {
public static class Preferences {
public static final String KEY_RUN_ON_BOOT ="run_on_boot";
public static final String KEY_WAKELOCK ="wakelock";
public static final String KEY_PAUSE_ON_HEADPHONES_DISCONNECT ="pause_on_headphones_disconnect";
public static SharedPreferences get(Context context) {
return context.getSharedPreferences(TAG, MODE_PRIVATE);
@@ -154,6 +155,9 @@ public class Settings extends Activity {
if (Preferences.getBoolean(Settings.this,
Preferences.KEY_WAKELOCK, false))
mClient.setWakelockEnabled(true);
if (Preferences.getBoolean(Settings.this,
Preferences.KEY_PAUSE_ON_HEADPHONES_DISCONNECT, false))
mClient.setPauseOnHeadphonesDisconnect(true);
} else {
mClient.stop();
}
@@ -179,6 +183,15 @@ public class Settings extends Activity {
}
};
private final OnCheckedChangeListener mOnPauseOnHeadphonesDisconnectChangeListener = new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Preferences.putBoolean(Settings.this, Preferences.KEY_PAUSE_ON_HEADPHONES_DISCONNECT, isChecked);
if (mClient != null && mClient.isRunning())
mClient.setPauseOnHeadphonesDisconnect(isChecked);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
/* TODO: this sure is the wrong place to request
@@ -211,6 +224,11 @@ public class Settings extends Activity {
if (Preferences.getBoolean(this, Preferences.KEY_WAKELOCK, false))
checkbox.setChecked(true);
checkbox = (CheckBox) findViewById(R.id.pause_on_headphones_disconnect);
checkbox.setOnCheckedChangeListener(mOnPauseOnHeadphonesDisconnectChangeListener);
if (Preferences.getBoolean(this, Preferences.KEY_PAUSE_ON_HEADPHONES_DISCONNECT, false))
checkbox.setChecked(true);
super.onCreate(savedInstanceState);
}

66
doc/client.rst Normal file

@@ -0,0 +1,66 @@
Client Developer's Manual
#########################
Introduction
************
MPD is a music player without a user interface. The user interface
will be provided by independent clients, which connect to MPD over
socket connections (TCP or local sockets).
This chapter describes how to develop a client.
Before you develop a new client, consider joining an existing client
project. There are many clients, but few are mature; we need fewer,
but better clients.
Client Libraries
****************
There are many libraries which help with connecting to MPD. If you
develop a MPD client, use a library instead of reinventing the wheel.
The MPD website has a list of libraries: https://www.musicpd.org/libs/
Connecting to MPD
*****************
Do not hard-code your client to connect to ``localhost:6600``.
Instead, use the defaults of the client library. For example, with
:program:`libmpdclient`, don't do::
c = mpd_connection_new("localhost", 6600, 30000);
Instead, do::
c = mpd_connection_new(NULL, 0, 0);
This way, the library can choose the best defaults, maybe derived from
environment variables, so all MPD clients use the same settings.
If you need to reimplement those defaults (or if you are developing a
client library), this is a good set of addresses to attempt to connect
to:
- if the environment variable :envvar:`MPD_HOST` is set:
``$MPD_HOST:$MPD_PORT`` (:envvar:`MPD_PORT` defaulting to 6600)
- if the environment variable :envvar:`XDG_RUNTIME_DIR` is set:
``$XDG_RUNTIME_DIR/mpd/socket``
- :file:`/run/mpd/socket`
- ``localhost:$MPD_PORT`` (:envvar:`MPD_PORT` defaulting to 6600)
Environment Variables
*********************
The following environment variables should be obeyed by all clients
(preferably by the client library):
- :envvar:`MPD_HOST`: the host (or local socket path) to connect to;
on Linux, this may start with a ``@`` to connect to an abstract
socket. To use a password with MPD, set :envvar:`MPD_HOST` to
``password@host`` (then abstract socket requires double ``@``:
``password@@socket``).
- :envvar:`MPD_PORT`: the port number; defaults to 6600.
- :envvar:`MPD_TIMEOUT`: timeout for connecting to MPD and for waiting
for MPD's response in seconds. A good default is 30 seconds.

@@ -30,7 +30,7 @@ master_doc = 'index'
# General information about the project.
project = 'Music Player Daemon'
copyright = '2003-2020 The Music Player Daemon Project'
copyright = '2003-2021 The Music Player Daemon Project'
author = 'Max Kellermann'
# The version info for the project you're documenting, acts as replacement for
@@ -38,9 +38,9 @@ author = 'Max Kellermann'
# built documents.
#
# The short X.Y version.
version = '0.22.8'
version = '0.23.1'
# The full version, including alpha/beta/rc tags.
release = version
#release = version + '~git'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

@@ -8,6 +8,7 @@ Music Player Daemon
user
plugins
developer
client
protocol

@@ -14,6 +14,7 @@ if get_option('html_manual')
input: [
'index.rst', 'user.rst', 'developer.rst',
'plugins.rst',
'client.rst',
'protocol.rst',
'conf.py',
],

@@ -23,11 +23,23 @@ The default plugin. Stores a copy of the database in memory. A file is used for
- The path of the cache directory for additional storages mounted at runtime. This setting is necessary for the **mount** protocol command.
* - **compress yes|no**
- Compress the database file using gzip? Enabled by default (if built with zlib).
* - **hide_playlist_targets yes|no**
- Hide songs which are referenced by playlists? Thas is,
playlist files which are represented in the database as virtual
directories (playlist plugin setting ``as_directory``). This
option is enabled by default and avoids duplicate songs; one
copy for the original file, and another copy in the virtual
directory of a CUE file referring to it.
proxy
-----
Provides access to the database of another :program:`MPD` instance using libmpdclient. This is useful when you mount the music directory via NFS/SMB, and the file server already runs a :program:`MPD` instance. Only the file server needs to update the database.
Provides access to the database of another :program:`MPD` instance
using `libmpdclient
<https://www.musicpd.org/libs/libmpdclient/>`_. This is useful when
you mount the music directory via NFS/SMB, and the file server already
runs a :program:`MPD` (0.20 or newer) instance. Only the file server
needs to update the database.
.. list-table::
:widths: 20 80
@@ -210,6 +222,8 @@ will be in effect.
- Verify the peer's SSL certificate? `More information <http://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYPEER.html>`_.
* - **verify_host yes|no**
- Verify the certificate's name against host? `More information <http://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYHOST.html>`_.
* - **cacert**
- Set path to Certificate Authority (CA) bundle `More information <https://curl.se/libcurl/c/CURLOPT_CAINFO.html>`_.
ffmpeg
------
@@ -295,37 +309,6 @@ in the form ``qobuz://track/ID``, e.g.:
* - **format_id N**
- The `Qobuz format identifier <https://github.com/Qobuz/api-documentation/blob/master/endpoints/track/getFileUrl.md#parameters>`_, i.e. a number which chooses the format and quality to be requested from Qobuz. The default is "5" (320 kbit/s MP3).
tidal
-----
Play songs from the commercial streaming service `Tidal
<http://tidal.com/>`_. It plays URLs in the form ``tidal://track/ID``,
e.g.:
.. warning::
This plugin is currently defunct because Tidal has changed the
protocol and decided not to share documentation.
.. code-block:: none
mpc add tidal://track/59727857
.. list-table::
:widths: 20 80
:header-rows: 1
* - Setting
- Description
* - **token TOKEN**
- The Tidal application token. Since Tidal is unwilling to assign a token to MPD, this needs to be reverse-engineered from another (approved) Tidal client.
* - **username USERNAME**
- The Tidal user name.
* - **password PASSWORD**
- The Tidal password.
* - **audioquality Q**
- The Tidal "audioquality" parameter. Possible values: HI_RES, LOSSLESS, HIGH, LOW. Default is HIGH.
.. _decoder_plugins:
Decoder plugins
@@ -482,9 +465,39 @@ Module player based on MODPlug.
* - Setting
- Description
* - **resampling_mode nearest|linear|spline|fir**
- Sets the resampling mode. "nearest" disables interpolation (good for chiptunes). "linear" makes modplug use linear interpolation (fast, good quality). "spline" makes modplug use cubic spline interpolation (high quality). "fir" makes modplug use 8-tap fir filter (extremely high quality). Defaults to "fir".
* - **loop_count**
- Number of times to loop the module if it uses backward loops. Default is 0 which prevents looping. -1 loops forever.
openmpt
-------
Module player based on `libopenmpt <https://lib.openmpt.org>`_.
.. list-table::
:widths: 20 80
:header-rows: 1
* - Setting
- Description
* - **repeat_count**
- Set how many times the module repeats. -1: repeat forever. 0: play once, repeat zero times (the default). n>0: play once and repeat n times after that.
* - **stereo_separation**
- Sets the stereo separation. The supported value range is [0,200]. Defaults to 100.
* - **interpolation_filter 0|1|2|4|8**
- Sets the interpolation filter. 0: internal default. 1: no interpolation (zero order hold). 2: linear interpolation. 4: cubic interpolation. 8: windowed sinc with 8 taps. Defaults to 0.
* - **override_mptm_interp_filter yes|no**
- If `interpolation_filter` has been changed, setting this to yes will force all MPTM modules to use that interpolation filter. If set to no, MPTM modules will play with their own interpolation filter regardless of the value of `interpolation_filter`. Defaults to no.
* - **volume_ramping**
- Sets the amount of volume ramping done by the libopenmpt mixer. The default value is -1, which indicates a recommended default value. The meaningful value range is [-1..10]. A value of 0 completely disables volume ramping. This might cause clicks in sound output. Higher values imply slower/softer volume ramps.
* - **sync_samples yes|no**
- Syncs sample playback when seeking. Defaults to yes.
* - **emulate_amiga yes|no**
- Enables the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. Defaults to yes.
* - **emulate_amiga_type**
- Configures the filter type to use for the Amiga resampler. Supported values are: "auto": Filter type is chosen by the library and might change. This is the default. "a500": Amiga A500 filter. "a1200": Amiga A1200 filter. "unfiltered": BLEP synthesis without model-specific filters. The LED filter is ignored by this setting. This filter mode is considered to be experimental and might change in the future. Defaults to "auto". Requires libopenmpt 0.5 or higher.
mpcdec
------
@@ -579,6 +592,10 @@ Encodes into `FLAC <https://xiph.org/flac/>`_ (lossless).
- Description
* - **compression**
- Sets the libFLAC compression level. The levels range from 0 (fastest, least compression) to 8 (slowest, most compression).
* - **oggflac yes|no**
- Configures if the stream should be Ogg FLAC versus native FLAC. Defaults to "no" (use native FLAC).
* - **oggchaining yes|no**
- Configures if the stream should use Ogg Chaining for in-stream metadata. Defaults to "no". Setting this to "yes" also enables Ogg FLAC.
lame
----
@@ -643,11 +660,15 @@ Encodes into `Ogg Opus <http://www.opus-codec.org/>`_.
* - Setting
- Description
* - **bitrate**
- Sets the data rate in bit per second. The special value "auto" lets libopus choose a rate (which is the default), and "max" uses the maximum possible data rate.
- Sets the data rate in bits per second. The special value "auto" lets libopus choose a rate (which is the default), and "max" uses the maximum possible data rate.
* - **complexity**
- Sets the `Opus complexity <https://wiki.xiph.org/OpusFAQ#What_is_the_complexity_of_Opus.3F>`_.
* - **signal**
- Sets the Opus signal type. Valid values are "auto" (the default), "voice" and "music".
* - **vbr yes|no|constrained**
- Sets the vbr mode. Setting to "yes" (default) enables variable bitrate, "no" forces constant bitrate and frame sizes, "constrained" uses constant bitrate analogous to CBR in AAC and MP3.
* - **packet_loss**
- Sets the expected packet loss percentage. This value can be increased from the default "0" for a more redundant stream at the expense of quality.
* - **opustags yes|no**
- Configures how metadata is interleaved into the stream. If set to yes, then metadata is inserted using ogg stream chaining, as specified in :rfc:`7845`. If set to no (the default), then ogg stream chaining is avoided and other output-dependent method is used, if available.
@@ -715,7 +736,7 @@ A resampler using `libsamplerate <http://www.mega-nerd.com/SRC/>`_ a.k.a. Secret
* - Name
- Description
* - **type**
- The interpolator type. See below for a list of known types.
- The interpolator type. Defaults to :samp:`2`. See below for a list of known types.
The following converter types are provided by libsamplerate:
@@ -990,6 +1011,8 @@ On Linux, OSS has been superseded by ALSA. Use the ALSA output plugin :ref:`alsa
- Description
* - **device PATH**
- Sets the path of the PCM device. If not specified, then MPD will attempt to open /dev/sound/dsp and /dev/dsp.
* - **dop yes|no**
- If set to yes, then DSD over PCM according to the `DoP standard <http://dsd-guide.com/dop-open-standard>`_ is enabled. This wraps DSD samples in fake 24 bit PCM, and is understood by some DSD capable products, but may be harmful to other hardware. Therefore, the default is no and you can enable the option at your own risk.
The according hardware mixer plugin understands the following settings:
@@ -1052,6 +1075,26 @@ The pipe plugin starts a program and writes raw PCM data into its standard input
* - **command CMD**
- This command is invoked with the shell.
pipewire
--------
Connect to a `PipeWire <https://pipewire.org/>`_ server. Requires
``libpipewire``.
.. list-table::
:widths: 20 80
:header-rows: 1
* - Setting
- Description
* - **target NAME**
- Link to the given target. If not specified, let the PipeWire
manager select a target. To get a list of available targets,
type ``pw-cli dump short Node``
* - **remote NAME**
- The name of the remote to connect to. The default is
``pipewire-0``.
.. _pulse_plugin:
pulse
@@ -1107,8 +1150,6 @@ You must set a format.
- Sets the host name of the `ShoutCast <http://www.shoutcast.com/>`_ / `IceCast <http://icecast.org/>`_ server.
* - **port PORTNUMBER**
- Connect to this port number on the specified host.
* - **timeout SECONDS**
- 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**
@@ -1144,6 +1185,34 @@ audio API. Its primary use is local playback on Android, where
floating point samples.
snapcast
--------
Snapcast is a multiroom client-server audio player. This plugin
allows MPD to act as a `Snapcast
<https://github.com/badaix/snapcast>`__ server. Snapcast clients
connect to it and receive audio data from MPD.
You must set a format.
.. list-table::
:widths: 20 80
:header-rows: 1
* - Setting
- Description
* - **port P**
- Binds the Snapcast server to the specified port. The default
port is :samp:`1704`.
* - **bind_to_address ADDR**
- Binds the Snapcast server to the specified address. Multiple
addresses in parallel are not supported. The default is to
bind on all addresses on port :samp:`1704`.
* - **zeroconf yes|no**
- Publish the Snapcast server as service type ``_snapcast._tcp``
via Zeroconf (Avahi or Bonjour). Default is :samp:`yes`.
solaris
-------
The "Solaris" plugin runs only on SUN Solaris, and plays via /dev/audio.
@@ -1200,7 +1269,7 @@ This plugin requires building with ``libavfilter`` (FFmpeg).
* - **graph "..."**
- Specifies the ``libavfilter`` graph; read the `FFmpeg
documentation
<https://libav.org/documentation/libavfilter.html#Filtergraph-syntax-1>`_
<https://ffmpeg.org/ffmpeg-filters.html#Filtergraph-syntax-1>`_
for details

@@ -286,10 +286,15 @@ The following tags are supported by :program:`MPD`:
* **date**: the song's release date. This is usually a 4-digit year.
* **originaldate**: the song's original release date.
* **composer**: the artist who composed the song.
* **composersort**: same as composer, but for sorting.
* **performer**: the artist who performed the song.
* **conductor**: the conductor who conducted the song.
* **work**: `"a work is a distinct intellectual or artistic creation,
which can be expressed in the form of one or more audio recordings" <https://musicbrainz.org/doc/Work>`_
* **ensemble**: the ensemble performing this song, e.g. "Wiener Philharmoniker".
* **movement**: name of the movement, e.g. "Andante con moto".
* **movementnumber**: movement number, e.g. "2" or "II".
* **location**: location of the recording, e.g. "Royal Albert Hall".
* **grouping**: "used if the sound belongs to a larger category of
sounds/music" (`from the IDv2.4.0 TIT1 description
<http://id3.org/id3v2.4.0-frames>`_).
@@ -544,6 +549,18 @@ Playback options
Sets volume to ``VOL``, the range of
volume is 0-100.
.. _command_getvol:
:command:`getvol`
Read the volume. The result is a ``volume:`` line like in
:ref:`status <command_status>`. If there is no mixer, MPD will
emit an empty response. Example::
getvol
volume: 42
OK
.. _command_single:
:command:`single {STATE}` [#since_0_15]_
@@ -692,6 +709,13 @@ Whenever possible, ids should be used.
Id: 999
OK
If the second parameter is given, then the song is inserted at the
specified position. If the parameter starts with ``+`` or ``-``,
then it is relative to the current song; e.g. ``+0`` inserts right
after the current song and ``-0`` inserts right before the current
song (i.e. zero songs between the current song and the newly added
song).
.. _command_clear:
:command:`clear`
@@ -715,14 +739,22 @@ Whenever possible, ids should be used.
at ``START:END`` [#since_0_15]_ to ``TO``
in the playlist.
If ``TO`` starts with ``+`` or ``-``, then it is relative to the
current song; e.g. ``+0`` moves to right after the current song
and ``-0`` moves to right before the current song (i.e. zero songs
between the current song and the moved range).
.. _command_moveid:
:command:`moveid {FROM} {TO}`
Moves the song with ``FROM`` (songid) to
``TO`` (playlist index) in the
playlist. If ``TO`` is negative, it
is relative to the current song in the playlist (if
there is one).
playlist.
If ``TO`` starts with ``+`` or ``-``, then it is relative to the
current song; e.g. ``+0`` moves to right after the current song
and ``-0`` moves to right before the current song (i.e. zero songs
between the current song and the moved song).
.. _command_playlist:
@@ -885,11 +917,17 @@ remote playlists (absolute URI with a supported scheme).
.. _command_load:
:command:`load {NAME} [START:END]`
:command:`load {NAME} [START:END] [POSITION]`
Loads the playlist into the current queue. Playlist
plugins are supported. A range may be specified to load
only a part of the playlist.
The ``POSITION`` parameter specifies where the songs will be
inserted into the queue; it can be relative as described in
:ref:`addid <command_addid>`. (This requires specifying the range
as well; the special value `0:` can be used if the whole playlist
shall be loaded at a certain queue position.)
.. _command_playlistadd:
:command:`playlistadd {NAME} {URI}`
@@ -1022,11 +1060,11 @@ The music database
.. _command_findadd:
:command:`findadd {FILTER} [sort {TYPE}] [window {START:END}]`
:command:`findadd {FILTER} [sort {TYPE}] [window {START:END}] [position POS]`
Search the database for songs matching
``FILTER`` (see :ref:`Filters <filter_syntax>`) and add them to
the queue. Parameters have the same meaning as for
:ref:`find <command_find>`.
:ref:`find <command_find>` and :ref:`searchadd <command_searchadd>`.
.. _command_list:
@@ -1159,7 +1197,7 @@ The music database
.. _command_search:
:command:`search {FILTER} [sort {TYPE}] [window {START:END}]`
:command:`search {FILTER} [sort {TYPE}] [window {START:END}]
Search the database for songs matching
``FILTER`` (see :ref:`Filters <filter_syntax>`). Parameters
have the same meaning as for :ref:`find <command_find>`,
@@ -1167,13 +1205,16 @@ The music database
.. _command_searchadd:
:command:`searchadd {FILTER} [sort {TYPE}] [window {START:END}]`
:command:`searchadd {FILTER} [sort {TYPE}] [window {START:END}] [position POS]`
Search the database for songs matching
``FILTER`` (see :ref:`Filters <filter_syntax>`) and add them to
the queue.
Parameters have the same meaning as for :ref:`search <command_search>`.
The ``position`` parameter specifies where the songs will be
inserted.
.. _command_searchaddpl:
:command:`searchaddpl {NAME} {FILTER} [sort {TYPE}] [window {START:END}]`

@@ -55,8 +55,8 @@ and unpack it (or `clone the git repository
In any case, you need:
* a C++17 compiler (e.g. GCC 8 or clang 5)
* `Meson 0.49.0 <http://mesonbuild.com/>`__ and `Ninja
* a C++17 compiler (e.g. GCC 8 or clang 7)
* `Meson 0.56.0 <http://mesonbuild.com/>`__ and `Ninja
<https://ninja-build.org/>`__
* Boost 1.58
* pkg-config
@@ -69,6 +69,7 @@ For example, the following installs a fairly complete list of build dependencies
.. code-block:: none
apt install meson g++ \
libfmt-dev \
libpcre3-dev \
libmad0-dev libmpg123-dev libid3tag0-dev \
libflac-dev libvorbis-dev libopus-dev libogg-dev \
@@ -110,6 +111,19 @@ The following command shows a list of compile-time options:
meson configure output/release
NB: Check the sysconfdir setting to determine where mpd will look for mpd.conf; if you expect mpd to look for /etc/mpd.conf the sysconfdir must be '/etc' (i.e., not 'etc' which will result in mpd looking for /usr/local/etc/mpd.conf):
.. code-block:: none
meson configure output/release |grep sysconfdir
If this is not /etc (or another path you wish to specify):
.. code-block:: none
$ meson configure output/release -Dsysconfdir='/etc' ; meson configure output/release |grep syscon
sysconfdir /etc Sysconf data directory
When everything is ready and configured, compile:
.. code-block:: none
@@ -144,7 +158,7 @@ This section is about the latter.
You need:
* `mingw-w64 <http://mingw-w64.org/doku.php>`__
* `Meson 0.49.0 <http://mesonbuild.com/>`__ and `Ninja
* `Meson 0.56.0 <http://mesonbuild.com/>`__ and `Ninja
<https://ninja-build.org/>`__
* cmake
* pkg-config
@@ -176,8 +190,8 @@ Compiling for Android
You need:
* Android SDK
* `Android NDK r22 <https://developer.android.com/ndk/downloads>`_
* `Meson 0.49.0 <http://mesonbuild.com/>`__ and `Ninja
* `Android NDK r23 <https://developer.android.com/ndk/downloads>`_
* `Meson 0.56.0 <http://mesonbuild.com/>`__ and `Ninja
<https://ninja-build.org/>`__
* cmake
* pkg-config
@@ -625,13 +639,20 @@ By default, all clients are unauthenticated and have a full set of permissions.
- Allows reading of the database, displaying the current playlist, and current status of :program:`MPD`.
* - **add**
- Allows adding songs and loading playlists.
* - **player**
- Allows any player and queue manipulation (start/pause/stop
playback etc.).
* - **control**
- Allows all other player and playlist manipulations.
* - **admin**
- Allows database updates and allows shutting down :program:`MPD`.
- Allows manipulating outputs, stickers and partitions,
mounting/unmounting storage and shutting down :program:`MPD`.
:code:`local_permissions` may be used to assign other permissions to clients connecting on a local socket.
:code:`host_permissions` may be used to assign permissions to clients
with a certain IP address.
:code:`password` allows the client to send a password to gain other permissions. This option may be specified multiple times with different passwords.
Note that the :code:`password` option is not secure: passwords are sent in clear-text over the connection, and the client cannot verify the server's identity.
@@ -641,6 +662,8 @@ Example:
.. code-block:: none
default_permissions "read"
host_permissions "192.168.0.100 read,add,control,admin"
host_permissions "2003:1234:4567::1 read,add,control,admin"
password "the_password@read,add,control"
password "the_admin_password@read,add,control,admin"
@@ -688,6 +711,8 @@ The State File
- Specify the state file location. The parent directory must be writable by the :program:`MPD` user (+wx).
* - **state_file_interval SECONDS**
- Auto-save the state file this number of seconds after each state change. Defaults to 120 (2 minutes).
* - **restore_paused yes|no**
- If set to :samp:`yes`, then :program:`MPD` is put into pause mode instead of starting playback after startup. Default is :samp:`no`.
The Sticker Database
^^^^^^^^^^^^^^^^^^^^
@@ -1120,7 +1145,7 @@ Support
Getting Help
^^^^^^^^^^^^
The :program:`MPD` project runs a `forum <https://forum.musicpd.org/>`_ and an IRC channel (#mpd on Freenode) for requesting help. Visit the MPD help page for details on how to get help.
The :program:`MPD` project runs a `forum <https://forum.musicpd.org/>`_ and an IRC channel (#mpd on Libera.Chat) for requesting help. Visit the MPD help page for details on how to get help.
Common Problems
^^^^^^^^^^^^^^^
@@ -1202,6 +1227,34 @@ Your bug report should contain:
* relevant portions of the log file (:option:`--verbose`)
* be clear about what you expect MPD to do, and what is actually happening
.. _profiler:
Too Much CPU Usage
^^^^^^^^^^^^^^^^^^
If you believe MPD consumes too much CPU, `write a bug report
<https://github.com/MusicPlayerDaemon/MPD/issues>`_ with a profiling
information.
On Linux, this can be obtained with :program:`perf` (on Debian,
installed the package :file:`linux-perf`), for example::
perf record -p `pidof mpd`
Run this command while MPD consumes much CPU, let it run for a minute
or so, and stop it by pressing ``Ctrl-C``. Then type::
perf report >mpd_perf.txt
Upload the output file to the bug report.
.. note::
This requires having debug symbols for MPD and all relevant
libraries. See :ref:`crash` for details.
.. _crash:
MPD crashes
^^^^^^^^^^^

@@ -1,8 +1,8 @@
project(
'mpd',
['c', 'cpp'],
version: '0.22.8',
meson_version: '>= 0.49.0',
version: '0.23.1',
meson_version: '>= 0.56.0',
default_options: [
'c_std=c11',
'build.c_std=c11',
@@ -10,9 +10,21 @@ project(
'build.cpp_std=c++17',
'warning_level=3',
# This is only here to build subprojects as static libraries; MPD
# itself doesn't ship any libraries.
'default_library=static',
# If we build those libraries as Meson subproject, they shall be
# linked statically into the MPD executable.
'expat:default_library=static',
'fmt:default_library=static',
'gtest:default_library=static',
'sqlite3:default_library=static',
'vorbis:default_library=static',
# Not interested in compiler warnings from subprojects.
'expat:werror=false',
'expat:warning_level=0',
'fmt:warning_level=0',
'gtest:warning_level=0',
'sqlite3:warning_level=0',
'vorbis:warning_level=0',
],
license: 'GPLv2+',
)
@@ -24,15 +36,15 @@ c_compiler = meson.get_compiler('c')
if compiler.get_id() == 'gcc' and compiler.version().version_compare('<8')
warning('Your GCC version is too old. You need at least version 8.')
elif compiler.get_id() == 'clang' and compiler.version().version_compare('<5')
warning('Your clang version is too old. You need at least version 5.')
elif compiler.get_id() == 'clang' and compiler.version().version_compare('<7')
warning('Your clang version is too old. You need at least version 7.')
endif
version_conf = configuration_data()
version_conf.set_quoted('PACKAGE', meson.project_name())
version_conf.set_quoted('PACKAGE_NAME', meson.project_name())
version_conf.set_quoted('VERSION', meson.project_version())
version_conf.set_quoted('PROTOCOL_VERSION', '0.22.4')
version_conf.set_quoted('PROTOCOL_VERSION', '0.23.1')
configure_file(output: 'Version.h', configuration: version_conf)
conf = configuration_data()
@@ -42,57 +54,63 @@ common_cppflags = [
'-D_GNU_SOURCE',
]
common_cflags = [
]
common_cxxflags = [
test_global_common_flags = [
'-fvisibility=hidden',
]
test_common_flags = [
'-Wvla',
'-Wdouble-promotion',
'-fvisibility=hidden',
'-ffast-math',
'-ftree-vectorize',
'-Wcast-qual',
'-Wdouble-promotion',
'-Wmissing-declarations',
'-Wshadow',
'-Wunused',
'-Wvla',
'-Wwrite-strings',
# clang specific warning options:
'-Wunreachable-code-aggressive',
'-Wused-but-marked-unused',
]
test_global_cxxflags = test_global_common_flags + [
]
test_global_cflags = test_global_common_flags + [
]
test_cxxflags = test_common_flags + [
'-fno-threadsafe-statics',
'-fmerge-all-constants',
'-Wmissing-declarations',
'-Wshadow',
'-Wpointer-arith',
'-Wcast-qual',
'-Wwrite-strings',
'-Wsign-compare',
'-Wcomma',
'-Wcomma-subscript',
'-Wextra-semi',
'-Wmismatched-tags',
'-Woverloaded-virtual',
'-Wsign-promo',
'-Wvolatile',
'-Wvirtual-inheritance',
# a vtable without a dtor is just fine
'-Wno-non-virtual-dtor',
# clang specific warning options:
'-Wcomma',
'-Wheader-hygiene',
'-Winconsistent-missing-destructor-override',
'-Wunreachable-code-break',
'-Wunused',
'-Wused-but-marked-unused',
'-Wno-non-virtual-dtor',
]
if compiler.get_id() == 'clang'
# Workaround for clang bug
# https://bugs.llvm.org/show_bug.cgi?id=32611
test_cxxflags += '-funwind-tables'
if compiler.get_id() != 'gcc' or compiler.version().version_compare('>=9')
# The GCC 8 implementation of this flag is buggy: it complains even
# if "final" is present, which implies "override".
test_cxxflags += '-Wsuggest-override'
endif
test_cflags = test_common_flags + [
'-Wmissing-prototypes',
'-Wshadow',
'-Wpointer-arith',
'-Wstrict-prototypes',
'-Wcast-qual',
'-Wwrite-strings',
'-pedantic',
]
test_ldflags = [
@@ -104,11 +122,11 @@ test_ldflags = [
]
if get_option('buildtype') != 'debug'
test_cxxflags += [
test_global_cxxflags += [
'-ffunction-sections',
'-fdata-sections',
]
test_cflags += [
test_global_cflags += [
'-ffunction-sections',
'-fdata-sections',
]
@@ -118,15 +136,20 @@ if get_option('buildtype') != 'debug'
endif
if get_option('fuzzer')
fuzzer_flags = ['-fsanitize=fuzzer,address,undefined']
fuzzer_flags = ['-fsanitize=fuzzer']
if get_option('b_sanitize') == 'none'
fuzzer_flags += ['-fsanitize=address,undefined']
endif
add_global_arguments(fuzzer_flags, language: 'cpp')
add_global_arguments(fuzzer_flags, language: 'c')
add_global_link_arguments(fuzzer_flags, language: 'cpp')
endif
add_global_arguments(common_cxxflags + compiler.get_supported_arguments(test_cxxflags), language: 'cpp')
add_global_arguments(common_cflags + c_compiler.get_supported_arguments(test_cflags), language: 'c')
add_global_link_arguments(compiler.get_supported_link_arguments(test_ldflags), language: 'cpp')
add_global_arguments(compiler.get_supported_arguments(test_global_cxxflags), language: 'cpp')
add_global_arguments(c_compiler.get_supported_arguments(test_global_cflags), language: 'c')
add_project_arguments(compiler.get_supported_arguments(test_cxxflags), language: 'cpp')
add_project_arguments(c_compiler.get_supported_arguments(test_cflags), language: 'c')
add_project_link_arguments(compiler.get_supported_link_arguments(test_ldflags), language: 'cpp')
is_linux = host_machine.system() == 'linux'
is_android = get_option('android_ndk') != ''
@@ -193,17 +216,6 @@ conf.set('HAVE_STRCASESTR', compiler.has_function('strcasestr'))
conf.set('HAVE_PRCTL', is_linux)
conf.set('USE_EVENTFD', is_linux and get_option('eventfd'))
conf.set('USE_SIGNALFD', is_linux and get_option('signalfd'))
if is_windows
conf.set('USE_WINSELECT', true)
elif is_linux and get_option('epoll')
conf.set('USE_EPOLL', true)
else
conf.set('USE_POLL', true)
endif
if not get_option('syslog').disabled()
if compiler.has_function('syslog')
conf.set('HAVE_SYSLOG', true)
@@ -234,15 +246,19 @@ if boost_dep.version() == '1.67'
warning('Your Boost version 1.67 is known to be buggy, and the MPD build will fail. Please upgrade to Boost 1.68 or later.')
endif
fmt_dep = dependency('fmt', fallback: ['fmt', 'fmt_dep'])
log = static_library(
'log',
'src/Log.cxx',
'src/LogBackend.cxx',
include_directories: inc,
dependencies: fmt_dep,
)
log_dep = declare_dependency(
link_with: log,
dependencies: fmt_dep,
)
sources = [
@@ -251,6 +267,7 @@ sources = [
'src/protocol/ArgParser.cxx',
'src/protocol/Result.cxx',
'src/command/CommandError.cxx',
'src/command/PositionArg.cxx',
'src/command/AllCommands.cxx',
'src/command/QueueCommands.cxx',
'src/command/TagCommands.cxx',
@@ -265,7 +282,6 @@ sources = [
'src/command/CommandListBuilder.cxx',
'src/Idle.cxx',
'src/IdleFlags.cxx',
'src/decoder/Domain.cxx',
'src/decoder/Thread.cxx',
'src/decoder/Control.cxx',
'src/decoder/Bridge.cxx',
@@ -396,6 +412,7 @@ subdir('src/lib/gcrypt')
subdir('src/lib/nfs')
subdir('src/lib/oss')
subdir('src/lib/pcre')
subdir('src/lib/pipewire')
subdir('src/lib/pulse')
subdir('src/lib/sndio')
subdir('src/lib/sqlite')
@@ -405,6 +422,8 @@ subdir('src/lib/yajl')
subdir('src/lib/crypto')
subdir('src/zeroconf')
subdir('src/fs')
subdir('src/config')
subdir('src/tag')
@@ -420,7 +439,6 @@ subdir('src/decoder')
subdir('src/encoder')
subdir('src/song')
subdir('src/playlist')
subdir('src/zeroconf')
if curl_dep.found()
sources += 'src/RemoteTagCache.cxx'
@@ -536,6 +554,7 @@ mpd = build_target(
zeroconf_dep,
more_deps,
chromaprint_dep,
fmt_dep,
],
link_args: link_args,
build_by_default: not get_option('fuzzer'),

@@ -62,7 +62,10 @@ option('dsd', type: 'boolean', value: true, description: 'Support the DSD audio
#
option('database', type: 'boolean', value: true, description: 'enable support for the music database')
option('upnp', type: 'feature', description: 'UPnP client support')
option('upnp', type: 'combo',
choices: ['auto', 'pupnp', 'npupnp', 'disabled'],
value: 'auto',
description: 'UPnP client support')
option('libmpdclient', type: 'feature', description: 'libmpdclient support (for the proxy database plugin)')
#
@@ -104,7 +107,6 @@ option('smbclient', type: 'feature', value: 'disabled', description: 'SMB suppor
option('qobuz', type: 'feature', description: 'Qobuz client')
option('soundcloud', type: 'feature', description: 'SoundCloud client')
option('tidal', type: 'feature', description: 'Tidal client')
#
# Archive plugins
@@ -135,6 +137,7 @@ option('gme', type: 'feature', description: 'Game Music Emulator decoder plugin'
option('mad', type: 'feature', description: 'MP3 decoder using libmad')
option('mikmod', type: 'feature', description: 'MikMod decoder plugin')
option('modplug', type: 'feature', description: 'Modplug decoder plugin')
option('openmpt', type: 'feature', description: 'OpenMPT decoder plugin')
option('mpcdec', type: 'feature', description: 'Musepack decoder plugin')
option('mpg123', type: 'feature', description: 'MP3 decoder using libmpg123')
option('opus', type: 'feature', description: 'Opus decoder plugin')
@@ -174,9 +177,11 @@ option('jack', type: 'feature', description: 'JACK output plugin')
option('openal', type: 'feature', description: 'OpenAL output plugin')
option('oss', type: 'feature', description: 'Open Sound System support')
option('pipe', type: 'boolean', value: true, description: 'Pipe output plugin')
option('pipewire', type: 'feature', description: 'PipeWire support')
option('pulse', type: 'feature', description: 'PulseAudio support')
option('recorder', type: 'boolean', value: true, description: 'Recorder output plugin')
option('shout', type: 'feature', description: 'Shoutcast streaming support using libshout')
option('snapcast', type: 'boolean', value: true, description: 'Snapcast output plugin')
option('sndio', type: 'feature', description: 'sndio output plugin')
option('solaris_output', type: 'feature', description: 'Solaris /dev/audio support')

@@ -55,10 +55,10 @@ class AutotoolsProject(MakeProject):
subprocess.check_call(configure, cwd=build, env=toolchain.env)
return build
def build(self, toolchain):
def _build(self, toolchain):
build = self.configure(toolchain)
if self.subdirs is not None:
for subdir in self.subdirs:
MakeProject.build(self, toolchain, os.path.join(build, subdir))
self.build_make(toolchain, os.path.join(build, subdir))
else:
MakeProject.build(self, toolchain, build)
self.build_make(toolchain, build)

@@ -12,7 +12,7 @@ class BoostProject(Project):
name='boost', version=version,
**kwargs)
def build(self, toolchain):
def _build(self, toolchain):
src = self.unpack(toolchain)
# install the headers manually; don't build any library

@@ -1,27 +1,63 @@
import os
import subprocess
from build.project import Project
def __write_cmake_compiler(f, language, compiler):
s = compiler.split(' ', 1)
if len(s) == 2:
print(f'set(CMAKE_{language}_COMPILER_LAUNCHER {s[0]})', file=f)
compiler = s[1]
print(f'set(CMAKE_{language}_COMPILER {compiler})', file=f)
def __write_cmake_toolchain_file(f, toolchain):
if '-darwin' in toolchain.actual_arch:
cmake_system_name = 'Darwin'
elif toolchain.is_windows:
cmake_system_name = 'Windows'
else:
cmake_system_name = 'Linux'
f.write(f"""
set(CMAKE_SYSTEM_NAME {cmake_system_name})
set(CMAKE_SYSTEM_PROCESSOR {toolchain.actual_arch.split('-', 1)[0]})
set(CMAKE_C_COMPILER_TARGET {toolchain.actual_arch})
set(CMAKE_CXX_COMPILER_TARGET {toolchain.actual_arch})
set(CMAKE_C_FLAGS "{toolchain.cflags} {toolchain.cppflags}")
set(CMAKE_CXX_FLAGS "{toolchain.cxxflags} {toolchain.cppflags}")
""")
__write_cmake_compiler(f, 'C', toolchain.cc)
__write_cmake_compiler(f, 'CXX', toolchain.cxx)
def configure(toolchain, src, build, args=()):
cross_args = []
if toolchain.is_windows:
cross_args.append('-DCMAKE_SYSTEM_NAME=Windows')
cross_args.append('-DCMAKE_RC_COMPILER=' + toolchain.windres)
# Several targets need a sysroot to prevent pkg-config from
# looking for libraries on the build host (TODO: fix this
# properly); but we must not do that on Android because the NDK
# has a sysroot already
if '-android' not in toolchain.actual_arch and '-darwin' not in toolchain.actual_arch:
cross_args.append('-DCMAKE_SYSROOT=' + toolchain.install_prefix)
os.makedirs(build, exist_ok=True)
cmake_toolchain_file = os.path.join(build, 'cmake_toolchain_file')
with open(cmake_toolchain_file, 'w') as f:
__write_cmake_toolchain_file(f, toolchain)
configure = [
'cmake',
src,
'-DCMAKE_TOOLCHAIN_FILE=' + cmake_toolchain_file,
'-DCMAKE_INSTALL_PREFIX=' + toolchain.install_prefix,
'-DCMAKE_BUILD_TYPE=release',
'-DCMAKE_C_COMPILER=' + toolchain.cc,
'-DCMAKE_CXX_COMPILER=' + toolchain.cxx,
'-DCMAKE_C_FLAGS=' + toolchain.cflags + ' ' + toolchain.cppflags,
'-DCMAKE_CXX_FLAGS=' + toolchain.cxxflags + ' ' + toolchain.cppflags,
'-GNinja',
] + cross_args + args
@@ -29,17 +65,22 @@ def configure(toolchain, src, build, args=()):
class CmakeProject(Project):
def __init__(self, url, md5, installed, configure_args=[],
windows_configure_args=[],
**kwargs):
Project.__init__(self, url, md5, installed, **kwargs)
self.configure_args = configure_args
self.windows_configure_args = windows_configure_args
def configure(self, toolchain):
src = self.unpack(toolchain)
build = self.make_build_path(toolchain)
configure(toolchain, src, build, self.configure_args)
configure_args = self.configure_args
if toolchain.is_windows:
configure_args = configure_args + self.windows_configure_args
configure(toolchain, src, build, configure_args)
return build
def build(self, toolchain):
def _build(self, toolchain):
build = self.configure(toolchain)
subprocess.check_call(['ninja', 'install'],
cwd=build, env=toolchain.env)

@@ -10,7 +10,7 @@ class FfmpegProject(Project):
self.configure_args = configure_args
self.cppflags = cppflags
def build(self, toolchain):
def _build(self, toolchain):
src = self.unpack(toolchain)
build = self.make_build_path(toolchain)

@@ -26,7 +26,7 @@ class JackProject(Project):
base='jack2-' + self.version,
**kwargs)
def build(self, toolchain):
def _build(self, toolchain):
src = self.unpack(toolchain)
includes = ['jack.h', 'ringbuffer.h', 'systemdeps.h', 'transport.h', 'types.h', 'weakmacros.h']

@@ -17,29 +17,17 @@ libmpdclient = MesonProject(
'lib/libmpdclient.a',
)
libogg = AutotoolsProject(
libogg = CmakeProject(
'http://downloads.xiph.org/releases/ogg/libogg-1.3.4.tar.xz',
'c163bc12bc300c401b6aa35907ac682671ea376f13ae0969a220f7ddf71893fe',
'lib/libogg.a',
[
'--disable-shared', '--enable-static',
'-DBUILD_SHARED_LIBS=OFF',
'-DINSTALL_DOCS=OFF',
'-DINSTALL_CMAKE_PACKAGE_MODULE=OFF',
],
)
libvorbis = AutotoolsProject(
'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.7.tar.xz',
'b33cc4934322bcbf6efcbacf49e3ca01aadbea4114ec9589d1b1e9d20f72954b',
'lib/libvorbis.a',
[
'--disable-shared', '--enable-static',
],
edits={
# this option is not understood by clang
'configure': lambda data: data.replace('-mno-ieee-fp', ' '),
}
)
opus = AutotoolsProject(
'https://archive.mozilla.org/pub/opus/opus-1.3.1.tar.gz',
'65b58e1e25b2a114157014736a3d9dfeaad8d41be1c8179866f144a2fb44ff9d',
@@ -123,18 +111,27 @@ libmodplug = AutotoolsProject(
],
)
libopenmpt = AutotoolsProject(
'https://lib.openmpt.org/files/libopenmpt/src/libopenmpt-0.5.8+release.autotools.tar.gz',
'61de7cc0c011b10472ca16adcc123689',
'lib/libopenmpt.a',
[
'--disable-shared', '--enable-static'
],
)
wildmidi = CmakeProject(
'https://codeload.github.com/Mindwerks/wildmidi/tar.gz/wildmidi-0.4.3',
'498e5a96455bb4b91b37188ad6dcb070824e92c44f5ed452b90adbaec8eef3c5',
'https://codeload.github.com/Mindwerks/wildmidi/tar.gz/wildmidi-0.4.4',
'6f267c8d331e9859906837e2c197093fddec31829d2ebf7b958cf6b7ae935430',
'lib/libWildMidi.a',
[
'-DBUILD_SHARED_LIBS=OFF',
'-DWANT_PLAYER=OFF',
'-DWANT_STATIC=ON',
],
base='wildmidi-wildmidi-0.4.3',
base='wildmidi-wildmidi-0.4.4',
name='wildmidi',
version='0.4.3',
version='0.4.4',
)
gme = CmakeProject(
@@ -379,53 +376,46 @@ ffmpeg = FfmpegProject(
)
openssl = OpenSSLProject(
'https://www.openssl.org/source/openssl-3.0.0-alpha16.tar.gz',
'08ce8244b59d75f40f91170dfcb012bf25309cdcb1fef9502e39d694f883d1d1',
'https://www.openssl.org/source/openssl-3.0.0.tar.gz',
'59eedfcb46c25214c9bd37ed6078297b4df01d012267fe9e9eee31f61bc70536',
'include/openssl/ossl_typ.h',
)
curl = AutotoolsProject(
'https://curl.se/download/curl-7.76.1.tar.xz',
'64bb5288c39f0840c07d077e30d9052e1cbb9fa6c2dc52523824cc859e679145',
curl = CmakeProject(
'https://curl.se/download/curl-7.79.1.tar.xz',
'0606f74b1182ab732a17c11613cbbaf7084f2e6cca432642d0e3ad7c224c3689',
'lib/libcurl.a',
[
'--disable-shared', '--enable-static',
'--disable-debug',
'--enable-http',
'--enable-ipv6',
'--disable-ftp', '--disable-file',
'--disable-ldap', '--disable-ldaps',
'--disable-rtsp', '--disable-proxy', '--disable-dict', '--disable-telnet',
'--disable-tftp', '--disable-pop3', '--disable-imap', '--disable-smtp',
'--disable-smb',
'--disable-gopher',
'--disable-manual',
'--disable-threaded-resolver', '--disable-verbose', '--disable-sspi',
'--disable-crypto-auth', '--disable-ntlm-wb', '--disable-tls-srp', '--disable-cookies',
'--disable-doh',
'--disable-mime',
'--disable-netrc',
'--disable-progress-meter',
'--disable-alt-svc',
'--without-gnutls', '--without-nss', '--without-libssh2',
# native Windows SSL/TLS support, option ignored on non-Windows builds
'--with-schannel',
'-DBUILD_CURL_EXE=OFF',
'-DBUILD_SHARED_LIBS=OFF',
'-DCURL_DISABLE_VERBOSE_STRINGS=ON',
'-DCURL_DISABLE_LDAP=ON',
'-DCURL_DISABLE_TELNET=ON',
'-DCURL_DISABLE_DICT=ON',
'-DCURL_DISABLE_FILE=ON',
'-DCURL_DISABLE_FTP=ON',
'-DCURL_DISABLE_TFTP=ON',
'-DCURL_DISABLE_LDAPS=ON',
'-DCURL_DISABLE_RTSP=ON',
'-DCURL_DISABLE_PROXY=ON',
'-DCURL_DISABLE_POP3=ON',
'-DCURL_DISABLE_IMAP=ON',
'-DCURL_DISABLE_SMTP=ON',
'-DCURL_DISABLE_GOPHER=ON',
'-DCURL_DISABLE_COOKIES=ON',
'-DCURL_DISABLE_CRYPTO_AUTH=ON',
'-DCURL_DISABLE_ALTSVC=ON',
'-DCMAKE_USE_LIBSSH2=OFF',
'-DCURL_WINDOWS_SSPI=OFF',
'-DCURL_DISABLE_NTLM=ON',
'-DBUILD_TESTING=OFF',
],
windows_configure_args=[
'-DCMAKE_USE_SCHANNEL=ON',
],
patches='src/lib/curl/patches',
)
libexpat = AutotoolsProject(
'https://github.com/libexpat/libexpat/releases/download/R_2_2_9/expat-2.2.9.tar.bz2',
'f1063084dc4302a427dabcca499c8312b3a32a29b7d2506653ecc8f950a9a237',
'lib/libexpat.a',
[
'--disable-shared', '--enable-static',
'--without-docbook',
],
)
libnfs = AutotoolsProject(
'https://github.com/sahlberg/libnfs/archive/libnfs-4.0.0.tar.gz',
'6ee77e9fe220e2d3e3b1f53cfea04fb319828cc7dbb97dd9df09e46e901d797d',
@@ -451,7 +441,7 @@ jack = JackProject(
)
boost = BoostProject(
'https://boostorg.jfrog.io/artifactory/main/release/1.76.0/source/boost_1_76_0.tar.bz2',
'f0397ba6e982c4450f27bf32a2a83292aba035b827a5623a14636ea583318c41',
'https://boostorg.jfrog.io/artifactory/main/release/1.77.0/source/boost_1_77_0.tar.bz2',
'fc9f85fc030e233142908241af7a846e60630aa7388de9a5fafb1f3a26840854',
'include/boost/version.hpp',
)

@@ -22,7 +22,7 @@ class MakeProject(Project):
subprocess.check_call(['/usr/bin/make'] + args,
cwd=wd, env=toolchain.env)
def build(self, toolchain, wd, install=True):
def build_make(self, toolchain, wd, install=True):
self.make(toolchain, wd, self.get_make_args(toolchain))
if install:
self.make(toolchain, wd, self.get_make_install_args(toolchain))

@@ -1,4 +1,5 @@
import os.path, subprocess, sys
import platform
from build.project import Project
@@ -34,41 +35,46 @@ def make_cross_file(toolchain):
path = os.path.join(toolchain.build_path, 'meson.cross')
os.makedirs(toolchain.build_path, exist_ok=True)
with open(path, 'w') as f:
f.write("""
f.write(f"""
[binaries]
c = '%s'
cpp = '%s'
ar = '%s'
strip = '%s'
pkgconfig = '%s'
%s
c = '{toolchain.cc}'
cpp = '{toolchain.cxx}'
ar = '{toolchain.ar}'
strip = '{toolchain.strip}'
pkgconfig = '{toolchain.pkg_config}'
""")
if toolchain.is_windows and platform.system() != 'Windows':
f.write(f"windres = '{toolchain.windres}'\n")
# Run unit tests with WINE when cross-building for Windows
print("exe_wrapper = 'wine'", file=f)
f.write(f"""
[properties]
root = '%s'
root = '{toolchain.install_prefix}'
c_args = %s
c_link_args = %s
[built-in options]
c_args = {repr((toolchain.cppflags + ' ' + toolchain.cflags).split())}
c_link_args = {repr(toolchain.ldflags.split() + toolchain.libs.split())}
cpp_args = %s
cpp_link_args = %s
cpp_args = {repr((toolchain.cppflags + ' ' + toolchain.cxxflags).split())}
cpp_link_args = {repr(toolchain.ldflags.split() + toolchain.libs.split())}
""")
if 'android' in toolchain.arch:
f.write("""
# Keep Meson from executing Android-x86 test binariees
needs_exe_wrapper = true
""")
f.write(f"""
[host_machine]
system = '%s'
cpu_family = '%s'
cpu = '%s'
endian = '%s'
""" % (toolchain.cc, toolchain.cxx, toolchain.ar, toolchain.strip,
toolchain.pkg_config,
windres,
toolchain.install_prefix,
repr((toolchain.cppflags + ' ' + toolchain.cflags).split()),
repr(toolchain.ldflags.split() + toolchain.libs.split()),
repr((toolchain.cppflags + ' ' + toolchain.cxxflags).split()),
repr(toolchain.ldflags.split() + toolchain.libs.split()),
system, cpu_family, cpu, endian))
system = '{system}'
cpu_family = '{cpu_family}'
cpu = '{cpu}'
endian = '{endian}'
""")
return path
def configure(toolchain, src, build, args=()):
@@ -79,11 +85,6 @@ def configure(toolchain, src, build, args=()):
'--prefix', toolchain.install_prefix,
# this is necessary because Meson uses Debian's build machine
# MultiArch path (e.g. "lib/x86_64-linux-gnu") for cross
# builds, which is obviously wrong
'--libdir', 'lib',
'--buildtype', 'plain',
'--default-library=static',
@@ -110,7 +111,7 @@ class MesonProject(Project):
configure(toolchain, src, build, self.configure_args)
return build
def build(self, toolchain):
def _build(self, toolchain):
build = self.configure(toolchain)
subprocess.check_call(['ninja', 'install'],
cwd=build, env=toolchain.env)

@@ -17,7 +17,13 @@ class OpenSSLProject(MakeProject):
'build_libs',
]
def build(self, toolchain):
def get_make_install_args(self, toolchain):
# OpenSSL's Makefile runs "ranlib" during installation
return MakeProject.get_make_install_args(self, toolchain) + [
'RANLIB=' + toolchain.ranlib,
]
def _build(self, toolchain):
src = self.unpack(toolchain, out_of_tree=False)
# OpenSSL has a weird target architecture scheme with lots of
@@ -52,4 +58,4 @@ class OpenSSLProject(MakeProject):
openssl_arch,
'--prefix=' + toolchain.install_prefix],
cwd=src, env=toolchain.env)
MakeProject.build(self, toolchain, src)
self.build_make(toolchain, src)

@@ -20,7 +20,7 @@ class Project:
self.base = base
if name is None or version is None:
m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?[\d.]*(?:-alpha\d+)?)$', self.base)
m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?[\d.]*(?:-(?:alpha|beta)\d+)?)(\+.*)?$', self.base)
if name is None: name = m.group(1)
if version is None: version = m.group(2)
@@ -79,3 +79,6 @@ class Project:
pass
os.makedirs(path, exist_ok=True)
return path
def build(self, toolchain):
self._build(toolchain)

@@ -31,6 +31,8 @@ def guess_digest_algorithm(digest):
return hashlib.sha1
elif l == 64:
return hashlib.sha256
elif l == 128:
return hashlib.sha512
else:
return None

@@ -7,7 +7,7 @@ class ZlibProject(Project):
**kwargs):
Project.__init__(self, url, md5, installed, **kwargs)
def build(self, toolchain):
def _build(self, toolchain):
src = self.unpack(toolchain, out_of_tree=False)
subprocess.check_call(['/usr/bin/make', '--quiet',

@@ -37,6 +37,7 @@
#include "fs/Traits.hxx"
#include "fs/FileSystem.hxx"
#include "fs/StandardDirectory.hxx"
#include "event/Features.h"
#include "io/uring/Features.h"
#include "util/Domain.hxx"
#include "util/OptionDef.hxx"

@@ -27,7 +27,7 @@
#include <cassert>
static const char *const idle_names[] = {
static constexpr const char * idle_names[] = {
"database",
"stored_playlist",
"playlist",
@@ -42,7 +42,7 @@ static const char *const idle_names[] = {
"neighbor",
"mount",
"partition",
nullptr
nullptr,
};
const char*const*

@@ -25,8 +25,6 @@
#ifndef MPD_IDLE_FLAGS_HXX
#define MPD_IDLE_FLAGS_HXX
#include "util/Compiler.h"
/** song database has been updated*/
static constexpr unsigned IDLE_DATABASE = 0x1;
@@ -73,7 +71,7 @@ static constexpr unsigned IDLE_PARTITION = 0x2000;
/**
* Get idle names
*/
gcc_const
[[gnu::const]]
const char*const*
idle_get_names() noexcept;
@@ -81,7 +79,7 @@ idle_get_names() noexcept;
* Parse an idle name and return its mask. Returns 0 if the given
* name is unknown.
*/
gcc_nonnull_all gcc_pure
[[gnu::nonnull]] [[gnu::pure]]
unsigned
idle_parse_name(const char *name) noexcept;

@@ -37,6 +37,10 @@
#include "db/update/Service.hxx"
#include "storage/StorageInterface.hxx"
#ifdef ENABLE_INOTIFY
#include "db/update/InotifyUpdate.hxx"
#endif
#ifdef ENABLE_NEIGHBOR_PLUGINS
#include "neighbor/Glue.hxx"
#endif

@@ -24,7 +24,6 @@
#include "event/Loop.hxx"
#include "event/Thread.hxx"
#include "event/MaskMonitor.hxx"
#include "util/Compiler.h"
#ifdef ENABLE_SYSTEMD_DAEMON
#include "lib/systemd/Watchdog.hxx"
@@ -44,6 +43,9 @@ class NeighborGlue;
#include "db/Ptr.hxx"
class Storage;
class UpdateService;
#ifdef ENABLE_INOTIFY
class InotifyUpdate;
#endif
#endif
#include <memory>
@@ -121,6 +123,10 @@ struct Instance final
Storage *storage = nullptr;
UpdateService *update = nullptr;
#ifdef ENABLE_INOTIFY
std::unique_ptr<InotifyUpdate> inotify_update;
#endif
#endif
#ifdef ENABLE_CURL
@@ -168,7 +174,7 @@ struct Instance final
* Find a #Partition with the given name. Returns nullptr if
* no such partition was found.
*/
gcc_pure
[[gnu::pure]]
Partition *FindPartition(const char *name) noexcept;
void DeletePartition(Partition &partition) noexcept;
@@ -194,7 +200,7 @@ struct Instance final
#endif
#ifdef ENABLE_SQLITE
bool HasStickerDatabase() noexcept {
bool HasStickerDatabase() const noexcept {
return sticker_database != nullptr;
}
#endif

@@ -25,13 +25,16 @@
#include "config/Data.hxx"
#include "config/Option.hxx"
#include "config/Net.hxx"
#include "lib/fmt/ExceptionFormatter.hxx"
#include "lib/fmt/PathFormatter.hxx"
#include "net/AllocatedSocketAddress.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "net/SocketUtil.hxx"
#include "system/Error.hxx"
#include "util/RuntimeError.hxx"
#include "fs/AllocatedPath.hxx"
#include "fs/XDG.hxx"
#include "util/Domain.hxx"
#include "util/RuntimeError.hxx"
#include <sys/stat.h>
@@ -41,6 +44,10 @@
#define DEFAULT_PORT 6600
#if defined(USE_XDG) && defined(HAVE_UN)
static constexpr Domain listen_domain("listen");
#endif
int listen_port;
#ifdef ENABLE_SYSTEMD_DAEMON
@@ -98,9 +105,9 @@ ListenXdgRuntimeDir(ClientListener &listener) noexcept
listener.AddFD(std::move(fd), std::move(address));
return true;
} catch (...) {
FormatError(std::current_exception(),
"Failed to listen on '%s' (not fatal)",
socket_path.c_str());
FmtError(listen_domain,
"Failed to listen on '{}' (not fatal): {}",
socket_path, std::current_exception());
return false;
}
#else

@@ -22,6 +22,7 @@
#include "client/Client.hxx"
#include "fs/AllocatedPath.hxx"
#include "ls.hxx"
#include "storage/Registry.hxx"
#include "util/ASCII.hxx"
#include "util/UriExtract.hxx"
@@ -67,9 +68,13 @@ LocateAbsoluteUri(UriPluginKind kind, const char *uri
{
switch (kind) {
case UriPluginKind::INPUT:
case UriPluginKind::STORAGE: // TODO: separate check for storage plugins
if (!uri_supported_scheme(uri))
throw std::runtime_error("Unsupported URI scheme");
throw std::invalid_argument("Unsupported URI scheme");
break;
case UriPluginKind::STORAGE:
/* plugin support will be checked after the
Storage::MapToRelativeUTF8() call */
break;
case UriPluginKind::PLAYLIST:
@@ -88,6 +93,10 @@ LocateAbsoluteUri(UriPluginKind kind, const char *uri
return LocatedUri(LocatedUri::Type::RELATIVE,
suffix.data());
}
if (kind == UriPluginKind::STORAGE &&
GetStoragePluginByUri(uri) == nullptr)
throw std::invalid_argument("Unsupported URI scheme");
#endif
return LocatedUri(LocatedUri::Type::ABSOLUTE, uri);
@@ -105,7 +114,7 @@ LocateUri(UriPluginKind kind,
const char *path_utf8 = StringAfterPrefixCaseASCII(uri, "file://");
if (path_utf8 != nullptr) {
if (!PathTraitsUTF8::IsAbsolute(path_utf8))
throw std::runtime_error("Malformed file:// URI");
throw std::invalid_argument("Malformed file:// URI");
return LocateFileUri(path_utf8, client
#ifdef ENABLE_DATABASE

@@ -17,166 +17,35 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "LogV.hxx"
#include "Log.hxx"
#include "lib/fmt/ExceptionFormatter.hxx"
#include "util/Domain.hxx"
#include "util/Exception.hxx"
#include <cerrno>
#include <stdio.h>
#include <string.h>
#include <fmt/format.h>
static constexpr Domain exception_domain("exception");
void
LogFormatV(LogLevel level, const Domain &domain,
const char *fmt, std::va_list ap) noexcept
LogVFmt(LogLevel level, const Domain &domain,
fmt::string_view format_str, fmt::format_args args) noexcept
{
char msg[1024];
vsnprintf(msg, sizeof(msg), fmt, ap);
Log(level, domain, msg);
}
void
LogFormat(LogLevel level, const Domain &domain, const char *fmt, ...) noexcept
{
std::va_list ap;
va_start(ap, fmt);
LogFormatV(level, domain, fmt, ap);
va_end(ap);
}
void
FormatDebug(const Domain &domain, const char *fmt, ...) noexcept
{
std::va_list ap;
va_start(ap, fmt);
LogFormatV(LogLevel::DEBUG, domain, fmt, ap);
va_end(ap);
}
void
FormatInfo(const Domain &domain, const char *fmt, ...) noexcept
{
std::va_list ap;
va_start(ap, fmt);
LogFormatV(LogLevel::INFO, domain, fmt, ap);
va_end(ap);
}
void
FormatNotice(const Domain &domain, const char *fmt, ...) noexcept
{
std::va_list ap;
va_start(ap, fmt);
LogFormatV(LogLevel::NOTICE, domain, fmt, ap);
va_end(ap);
}
void
FormatWarning(const Domain &domain, const char *fmt, ...) noexcept
{
std::va_list ap;
va_start(ap, fmt);
LogFormatV(LogLevel::WARNING, domain, fmt, ap);
va_end(ap);
}
void
FormatError(const Domain &domain, const char *fmt, ...) noexcept
{
std::va_list ap;
va_start(ap, fmt);
LogFormatV(LogLevel::ERROR, domain, fmt, ap);
va_end(ap);
}
void
Log(LogLevel level, const std::exception &e) noexcept
{
Log(level, exception_domain, GetFullMessage(e).c_str());
}
void
Log(LogLevel level, const std::exception &e, const char *msg) noexcept
{
LogFormat(level, exception_domain, "%s: %s", msg, GetFullMessage(e).c_str());
}
void
LogFormat(LogLevel level, const std::exception &e, const char *fmt, ...) noexcept
{
char msg[1024];
std::va_list ap;
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
Log(level, e, msg);
fmt::memory_buffer buffer;
#if FMT_VERSION >= 80000
fmt::vformat_to(std::back_inserter(buffer), format_str, args);
#else
fmt::vformat_to(buffer, format_str, args);
#endif
Log(level, domain, {buffer.data(), buffer.size()});
}
void
Log(LogLevel level, const std::exception_ptr &ep) noexcept
{
Log(level, exception_domain, GetFullMessage(ep).c_str());
Log(level, exception_domain, GetFullMessage(ep));
}
void
Log(LogLevel level, const std::exception_ptr &ep, const char *msg) noexcept
{
LogFormat(level, exception_domain, "%s: %s", msg,
GetFullMessage(ep).c_str());
}
void
LogFormat(LogLevel level, const std::exception_ptr &ep, const char *fmt, ...) noexcept
{
char msg[1024];
std::va_list ap;
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
Log(level, ep, msg);
}
void
LogErrno(const Domain &domain, int e, const char *msg) noexcept
{
LogFormat(LogLevel::ERROR, domain, "%s: %s", msg, strerror(e));
}
void
LogErrno(const Domain &domain, const char *msg) noexcept
{
LogErrno(domain, errno, msg);
}
static void
FormatErrnoV(const Domain &domain, int e, const char *fmt, std::va_list ap) noexcept
{
char msg[1024];
vsnprintf(msg, sizeof(msg), fmt, ap);
LogErrno(domain, e, msg);
}
void
FormatErrno(const Domain &domain, int e, const char *fmt, ...) noexcept
{
std::va_list ap;
va_start(ap, fmt);
FormatErrnoV(domain, e, fmt, ap);
va_end(ap);
}
void
FormatErrno(const Domain &domain, const char *fmt, ...) noexcept
{
const int e = errno;
std::va_list ap;
va_start(ap, fmt);
FormatErrnoV(domain, e, fmt, ap);
va_end(ap);
LogFmt(level, exception_domain, "{}: {}", msg, ep);
}

@@ -21,29 +21,80 @@
#define MPD_LOG_HXX
#include "LogLevel.hxx"
#include "util/Compiler.h"
#include <fmt/core.h>
#if FMT_VERSION < 70000 || FMT_VERSION >= 80000
#include <fmt/format.h>
#endif
#include <exception>
#include <string_view>
#include <utility>
class Domain;
void
Log(LogLevel level, const Domain &domain, const char *msg) noexcept;
gcc_printf(3,4)
void
LogFormat(LogLevel level, const Domain &domain, const char *fmt, ...) noexcept;
Log(LogLevel level, const Domain &domain, std::string_view msg) noexcept;
void
Log(LogLevel level, const std::exception &e) noexcept;
LogVFmt(LogLevel level, const Domain &domain,
fmt::string_view format_str, fmt::format_args args) noexcept;
template<typename S, typename... Args>
void
Log(LogLevel level, const std::exception &e, const char *msg) noexcept;
LogFmt(LogLevel level, const Domain &domain,
const S &format_str, Args&&... args) noexcept
{
#if FMT_VERSION >= 70000
return LogVFmt(level, domain, fmt::to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str,
args...));
#else
/* expensive fallback for older libfmt versions */
const auto result = fmt::format(format_str, args...);
return Log(level, domain, result);
#endif
}
gcc_printf(3,4)
template<typename S, typename... Args>
void
LogFormat(LogLevel level, const std::exception &e,
const char *fmt, ...) noexcept;
FmtDebug(const Domain &domain,
const S &format_str, Args&&... args) noexcept
{
LogFmt(LogLevel::DEBUG, domain, format_str, args...);
}
template<typename S, typename... Args>
void
FmtInfo(const Domain &domain,
const S &format_str, Args&&... args) noexcept
{
LogFmt(LogLevel::INFO, domain, format_str, args...);
}
template<typename S, typename... Args>
void
FmtNotice(const Domain &domain,
const S &format_str, Args&&... args) noexcept
{
LogFmt(LogLevel::NOTICE, domain, format_str, args...);
}
template<typename S, typename... Args>
void
FmtWarning(const Domain &domain,
const S &format_str, Args&&... args) noexcept
{
LogFmt(LogLevel::WARNING, domain, format_str, args...);
}
template<typename S, typename... Args>
void
FmtError(const Domain &domain,
const S &format_str, Args&&... args) noexcept
{
LogFmt(LogLevel::ERROR, domain, format_str, args...);
}
void
Log(LogLevel level, const std::exception_ptr &ep) noexcept;
@@ -51,76 +102,36 @@ Log(LogLevel level, const std::exception_ptr &ep) noexcept;
void
Log(LogLevel level, const std::exception_ptr &ep, const char *msg) noexcept;
gcc_printf(3,4)
void
LogFormat(LogLevel level, const std::exception_ptr &ep,
const char *fmt, ...) noexcept;
static inline void
LogDebug(const Domain &domain, const char *msg) noexcept
{
Log(LogLevel::DEBUG, domain, msg);
}
gcc_printf(2,3)
void
FormatDebug(const Domain &domain, const char *fmt, ...) noexcept;
static inline void
LogInfo(const Domain &domain, const char *msg) noexcept
{
Log(LogLevel::INFO, domain, msg);
}
gcc_printf(2,3)
void
FormatInfo(const Domain &domain, const char *fmt, ...) noexcept;
static inline void
LogNotice(const Domain &domain, const char *msg) noexcept
{
Log(LogLevel::NOTICE, domain, msg);
}
gcc_printf(2,3)
void
FormatNotice(const Domain &domain, const char *fmt, ...) noexcept;
static inline void
LogWarning(const Domain &domain, const char *msg) noexcept
{
Log(LogLevel::WARNING, domain, msg);
}
gcc_printf(2,3)
void
FormatWarning(const Domain &domain, const char *fmt, ...) noexcept;
static inline void
LogError(const Domain &domain, const char *msg) noexcept
{
Log(LogLevel::ERROR, domain, msg);
}
inline void
LogError(const std::exception &e) noexcept
{
Log(LogLevel::ERROR, e);
}
inline void
LogError(const std::exception &e, const char *msg) noexcept
{
Log(LogLevel::ERROR, e, msg);
}
template<typename... Args>
inline void
FormatError(const std::exception &e, const char *fmt, Args&&... args) noexcept
{
LogFormat(LogLevel::ERROR, e, fmt, std::forward<Args>(args)...);
}
inline void
LogError(const std::exception_ptr &ep) noexcept
{
@@ -133,30 +144,4 @@ LogError(const std::exception_ptr &ep, const char *msg) noexcept
Log(LogLevel::ERROR, ep, msg);
}
template<typename... Args>
inline void
FormatError(const std::exception_ptr &ep,
const char *fmt, Args&&... args) noexcept
{
LogFormat(LogLevel::ERROR, ep, fmt, std::forward<Args>(args)...);
}
gcc_printf(2,3)
void
FormatError(const Domain &domain, const char *fmt, ...) noexcept;
void
LogErrno(const Domain &domain, int e, const char *msg) noexcept;
void
LogErrno(const Domain &domain, const char *msg) noexcept;
gcc_printf(3,4)
void
FormatErrno(const Domain &domain, int e, const char *fmt, ...) noexcept;
gcc_printf(2,3)
void
FormatErrno(const Domain &domain, const char *fmt, ...) noexcept;
#endif /* LOG_H */

@@ -103,10 +103,9 @@ log_date() noexcept
* characters.
*/
static int
chomp_length(const char *p) noexcept
chomp_length(std::string_view p) noexcept
{
size_t length = strlen(p);
return StripRight(p, length);
return StripRight(p.data(), p.size());
}
#ifdef HAVE_SYSLOG
@@ -137,11 +136,11 @@ ToSysLogLevel(LogLevel log_level) noexcept
}
static void
SysLog(const Domain &domain, LogLevel log_level, const char *message) noexcept
SysLog(const Domain &domain, LogLevel log_level, std::string_view message) noexcept
{
syslog(ToSysLogLevel(log_level), "%s: %.*s",
domain.GetName(),
chomp_length(message), message);
chomp_length(message), message.data());
}
void
@@ -161,12 +160,12 @@ LogFinishSysLog() noexcept
#endif
static void
FileLog(const Domain &domain, const char *message) noexcept
FileLog(const Domain &domain, std::string_view message) noexcept
{
fprintf(stderr, "%s%s: %.*s\n",
enable_timestamp ? log_date() : "",
domain.GetName(),
chomp_length(message), message);
chomp_length(message), message.data());
#ifdef _WIN32
/* force-flush the log file, because setvbuf() does not seem
@@ -178,14 +177,20 @@ FileLog(const Domain &domain, const char *message) noexcept
#endif /* !ANDROID */
void
Log(LogLevel level, const Domain &domain, const char *msg) noexcept
Log(LogLevel level, const Domain &domain, std::string_view msg) noexcept
{
#ifdef ANDROID
__android_log_print(ToAndroidLogLevel(level), "MPD",
"%s: %s", domain.GetName(), msg);
if (logListener != nullptr)
"%s: %.*s", domain.GetName(),
(int)msg.size(), msg.data());
if (logListener != nullptr) {
char buffer[1024];
snprintf(buffer, sizeof(buffer), "%s: %.*s",
domain.GetName(), (int)msg.size(), msg.data());
logListener->OnLog(Java::GetEnv(), ToAndroidLogLevel(level),
"%s: %s", domain.GetName(), msg);
buffer);
}
#else
if (level < log_threshold)

@@ -234,22 +234,22 @@ cycle_log_files() noexcept
if (out_path.IsNull())
return 0;
FormatDebug(log_domain, "Cycling log files");
LogDebug(log_domain, "Cycling log files");
close_log_files();
fd = open_log_file();
if (fd < 0) {
const std::string out_path_utf8 = out_path.ToUTF8();
FormatError(log_domain,
"error re-opening log file: %s",
out_path_utf8.c_str());
FmtError(log_domain,
"error re-opening log file: {}",
out_path_utf8);
return -1;
}
redirect_logs(fd);
close(fd);
FormatDebug(log_domain, "Done cycling log files");
LogDebug(log_domain, "Done cycling log files");
return 0;
#endif
}

@@ -40,10 +40,11 @@
#include "input/cache/Config.hxx"
#include "input/cache/Manager.hxx"
#include "event/Loop.hxx"
#include "event/Call.hxx"
#include "fs/AllocatedPath.hxx"
#include "fs/Config.hxx"
#include "playlist/PlaylistRegistry.hxx"
#include "zeroconf/ZeroconfGlue.hxx"
#include "zeroconf/Glue.hxx"
#include "decoder/DecoderList.hxx"
#include "pcm/AudioParser.hxx"
#include "pcm/Convert.hxx"
@@ -157,7 +158,17 @@ glue_daemonize_init(const struct options *options,
static void
glue_mapper_init(const ConfigData &config)
{
mapper_init(config.GetPath(ConfigOption::PLAYLIST_DIR));
auto playlist_directory = config.GetPath(ConfigOption::PLAYLIST_DIR);
#ifdef ANDROID
/* if there is no explicit configuration, store playlists in
"/sdcard/Android/data/org.musicpd/files/playlists" */
if (playlist_directory.IsNull())
playlist_directory = context->GetExternalFilesDir(Java::GetEnv(),
"playlists");
#endif
mapper_init(std::move(playlist_directory));
}
#ifdef ENABLE_DATABASE
@@ -286,9 +297,8 @@ initialize_decoder_and_player(Instance &instance,
"positive integer", s);
if (result < MIN_BUFFER_SIZE) {
FormatWarning(config_domain, "buffer size %lu is too small, using %lu bytes instead",
(unsigned long)result,
(unsigned long)MIN_BUFFER_SIZE);
FmtWarning(config_domain, "buffer size {} is too small, using {} bytes instead",
result, MIN_BUFFER_SIZE);
result = MIN_BUFFER_SIZE;
}
@@ -334,7 +344,7 @@ Instance::BeginShutdownUpdate() noexcept
{
#ifdef ENABLE_DATABASE
#ifdef ENABLE_INOTIFY
mpd_inotify_finish();
inotify_update.reset();
#endif
if (update != nullptr)
@@ -441,7 +451,8 @@ MainConfigured(const struct options &options, const ConfigData &raw_config)
command_init();
for (auto &partition : instance.partitions) {
partition.outputs.Configure(instance.rtio_thread.GetEventLoop(),
partition.outputs.Configure(instance.io_thread.GetEventLoop(),
instance.rtio_thread.GetEventLoop(),
raw_config,
config.replay_gain);
partition.UpdateEffectiveReplayGainMode();
@@ -476,7 +487,27 @@ MainConfigured(const struct options &options, const ConfigData &raw_config)
};
#endif
ZeroconfInit(raw_config, instance.event_loop);
#ifdef HAVE_ZEROCONF
std::unique_ptr<ZeroconfHelper> zeroconf;
try {
auto &event_loop = instance.io_thread.GetEventLoop();
BlockingCall(event_loop, [&](){
zeroconf = ZeroconfInit(raw_config, event_loop);
});
} catch (...) {
LogError(std::current_exception(),
"Zeroconf initialization failed");
}
AtScopeExit(&zeroconf, &instance) {
if (zeroconf) {
auto &event_loop = instance.io_thread.GetEventLoop();
BlockingCall(event_loop, [&](){
zeroconf.reset();
});
}
};
#endif
#ifdef ENABLE_DATABASE
if (create_db) {
@@ -492,15 +523,21 @@ MainConfigured(const struct options &options, const ConfigData &raw_config)
if (raw_config.GetBool(ConfigOption::AUTO_UPDATE, false)) {
#ifdef ENABLE_INOTIFY
if (instance.storage != nullptr &&
instance.update != nullptr)
mpd_inotify_init(instance.event_loop,
*instance.storage,
*instance.update,
raw_config.GetUnsigned(ConfigOption::AUTO_UPDATE_DEPTH,
INT_MAX));
instance.update != nullptr) {
try {
instance.inotify_update =
mpd_inotify_init(instance.event_loop,
*instance.storage,
*instance.update,
raw_config.GetUnsigned(ConfigOption::AUTO_UPDATE_DEPTH,
INT_MAX));
} catch (...) {
LogError(std::current_exception());
}
}
#else
FormatWarning(config_domain,
"inotify: auto_update was disabled. enable during compilation phase");
LogWarning(config_domain,
"inotify: auto_update was disabled. enable during compilation phase");
#endif
}
#endif
@@ -537,9 +574,6 @@ MainConfigured(const struct options &options, const ConfigData &raw_config)
instance.state_file->Write();
instance.BeginShutdownUpdate();
ZeroconfDeinit();
instance.BeginShutdownPartitions();
}
@@ -594,6 +628,15 @@ Java_org_musicpd_Bridge_shutdown(JNIEnv *, jclass)
global_instance->Break();
}
gcc_visibility_default
JNIEXPORT void JNICALL
Java_org_musicpd_Bridge_pause(JNIEnv *, jclass)
{
if (global_instance != nullptr)
for (auto &partition : global_instance->partitions)
partition.pc.LockSetPause(true);
}
#else
static inline void
@@ -607,27 +650,26 @@ MainOrThrow(int argc, char *argv[])
MainConfigured(options, raw_config);
}
int mpd_main(int argc, char *argv[]) noexcept
int
mpd_main(int argc, char *argv[])
{
AtScopeExit() { log_deinit(); };
try {
MainOrThrow(argc, argv);
return EXIT_SUCCESS;
} catch (...) {
LogError(std::current_exception());
return EXIT_FAILURE;
}
MainOrThrow(argc, argv);
return EXIT_SUCCESS;
}
int
main(int argc, char *argv[]) noexcept
{
try {
AtScopeExit() { log_deinit(); };
#ifdef _WIN32
return win32_main(argc, argv);
#else
return mpd_main(argc, argv);
#endif
} catch (...) {
LogError(std::current_exception());
return EXIT_FAILURE;
}
#endif

@@ -40,7 +40,7 @@ extern Instance *global_instance;
* after doing some initialization.
*/
int
mpd_main(int argc, char *argv[]) noexcept;
mpd_main(int argc, char *argv[]);
#endif

@@ -24,7 +24,6 @@
#ifndef MPD_MAPPER_HXX
#define MPD_MAPPER_HXX
#include "util/Compiler.h"
#include "config.h"
#include <string>
@@ -44,7 +43,7 @@ mapper_init(AllocatedPath &&playlist_dir);
* is basically done by converting the URI to the file system charset
* and prepending the music directory.
*/
gcc_pure
[[gnu::pure]]
AllocatedPath
map_uri_fs(const char *uri) noexcept;
@@ -56,7 +55,7 @@ map_uri_fs(const char *uri) noexcept;
* @return the relative path in UTF-8, or an empty string if mapping
* failed
*/
gcc_pure
[[gnu::pure]]
std::string
map_fs_to_utf8(Path path_fs) noexcept;
@@ -65,7 +64,7 @@ map_fs_to_utf8(Path path_fs) noexcept;
/**
* Returns the playlist directory.
*/
gcc_const
[[gnu::const]]
const AllocatedPath &
map_spl_path() noexcept;
@@ -75,7 +74,7 @@ map_spl_path() noexcept;
*
* @return the path in file system encoding, or nullptr if mapping failed
*/
gcc_pure
[[gnu::pure]]
AllocatedPath
map_spl_utf8_to_fs(const char *name) noexcept;

@@ -20,8 +20,6 @@
#ifndef MPD_MIX_RAMP_INFO_HXX
#define MPD_MIX_RAMP_INFO_HXX
#include "util/Compiler.h"
#include <string>
class MixRampInfo {
@@ -35,17 +33,17 @@ public:
end.clear();
}
gcc_pure
[[gnu::pure]]
bool IsDefined() const noexcept {
return !start.empty() || !end.empty();
}
gcc_pure
[[gnu::pure]]
const char *GetStart() const noexcept {
return start.empty() ? nullptr : start.c_str();
}
gcc_pure
[[gnu::pure]]
const char *GetEnd() const noexcept {
return end.empty() ? nullptr : end.c_str();
}

@@ -63,7 +63,7 @@ public:
* is the same value which was passed to the constructor
* music_buffer_new().
*/
gcc_pure
[[gnu::pure]]
unsigned GetSize() const noexcept {
return buffer.GetCapacity();
}

@@ -105,7 +105,7 @@ struct MusicChunkInfo {
* Checks if the audio format if the chunk is equal to the
* specified audio_format.
*/
gcc_pure
[[gnu::pure]]
bool CheckFormat(AudioFormat audio_format) const noexcept;
#endif
};

@@ -22,7 +22,6 @@
#include "MusicChunkPtr.hxx"
#include "thread/Mutex.hxx"
#include "util/Compiler.h"
#ifndef NDEBUG
#include "pcm/AudioFormat.hxx"
@@ -59,7 +58,7 @@ public:
* Checks if the audio format if the chunk is equal to the specified
* audio_format.
*/
gcc_pure
[[gnu::pure]]
bool CheckFormat(AudioFormat other) const noexcept {
return !audio_format.IsDefined() ||
audio_format == other;
@@ -68,7 +67,7 @@ public:
/**
* Checks if the specified chunk is enqueued in the music pipe.
*/
gcc_pure
[[gnu::pure]]
bool Contains(const MusicChunk *chunk) const noexcept;
#endif
@@ -76,7 +75,7 @@ public:
* Returns the first #MusicChunk from the pipe. Returns
* nullptr if the pipe is empty.
*/
gcc_pure
[[gnu::pure]]
const MusicChunk *Peek() const noexcept {
const std::lock_guard<Mutex> protect(mutex);
return head.get();
@@ -100,13 +99,13 @@ public:
/**
* Returns the number of chunks currently in this pipe.
*/
gcc_pure
[[gnu::pure]]
unsigned GetSize() const noexcept {
const std::lock_guard<Mutex> protect(mutex);
return size;
}
gcc_pure
[[gnu::pure]]
bool IsEmpty() const noexcept {
return GetSize() == 0;
}

@@ -21,6 +21,7 @@
#include "Partition.hxx"
#include "Instance.hxx"
#include "Log.hxx"
#include "lib/fmt/ExceptionFormatter.hxx"
#include "song/DetachedSong.hxx"
#include "mixer/Volume.hxx"
#include "IdleFlags.hxx"
@@ -67,13 +68,14 @@ PrefetchSong(InputCacheManager &cache, const char *uri) noexcept
if (cache.Contains(uri))
return;
FormatDebug(cache_domain, "Prefetch '%s'", uri);
FmtDebug(cache_domain, "Prefetch '{}'", uri);
try {
cache.Prefetch(uri);
} catch (...) {
FormatError(std::current_exception(),
"Prefetch '%s' failed", uri);
FmtError(cache_domain,
"Prefetch '{}' failed: {}",
uri, std::current_exception());
}
}

@@ -27,6 +27,7 @@
#include "mixer/Listener.hxx"
#include "player/Control.hxx"
#include "player/Listener.hxx"
#include "protocol/RangeArg.hxx"
#include "ReplayGainMode.hxx"
#include "SingleMode.hxx"
#include "Chrono.hxx"
@@ -38,6 +39,7 @@
#include <memory>
struct Instance;
struct RangeArg;
class MultipleOutputs;
class SongLoader;
class ClientListener;
@@ -133,24 +135,20 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
* @param start the position of the first song to delete
* @param end the position after the last song to delete
*/
void DeleteRange(unsigned start, unsigned end) {
playlist.DeleteRange(pc, start, end);
void DeleteRange(RangeArg range) {
playlist.DeleteRange(pc, range);
}
void StaleSong(const char *uri) noexcept {
playlist.StaleSong(pc, uri);
}
void Shuffle(unsigned start, unsigned end) noexcept {
playlist.Shuffle(pc, start, end);
void Shuffle(RangeArg range) {
playlist.Shuffle(pc, range);
}
void MoveRange(unsigned start, unsigned end, int to) {
playlist.MoveRange(pc, start, end, to);
}
void MoveId(unsigned id, int to) {
playlist.MoveId(pc, id, to);
void MoveRange(RangeArg range, unsigned to) {
playlist.MoveRange(pc, range, to);
}
void SwapPositions(unsigned song1, unsigned song2) {
@@ -161,10 +159,8 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
playlist.SwapIds(pc, id1, id2);
}
void SetPriorityRange(unsigned start_position, unsigned end_position,
uint8_t priority) {
playlist.SetPriorityRange(pc, start_position, end_position,
priority);
void SetPriorityRange(RangeArg position_range, uint8_t priority) {
playlist.SetPriorityRange(pc, position_range, priority);
}
void SetPriorityId(unsigned song_id, uint8_t priority) {

@@ -22,6 +22,9 @@
#include "config/Param.hxx"
#include "config/Data.hxx"
#include "config/Option.hxx"
#include "net/AddressInfo.hxx"
#include "net/Resolver.hxx"
#include "net/ToString.hxx"
#include "util/IterableSplitString.hxx"
#include "util/RuntimeError.hxx"
#include "util/StringView.hxx"
@@ -41,6 +44,7 @@ static constexpr struct {
} permission_names[] = {
{ "read", PERMISSION_READ },
{ "add", PERMISSION_ADD },
{ "player", PERMISSION_PLAYER },
{ "control", PERMISSION_CONTROL },
{ "admin", PERMISSION_ADMIN },
{ nullptr, 0 },
@@ -54,6 +58,10 @@ static unsigned permission_default;
static unsigned local_permissions;
#endif
#ifdef HAVE_TCP
static std::map<std::string, unsigned> host_passwords;
#endif
static unsigned
ParsePermission(StringView s)
{
@@ -65,16 +73,20 @@ ParsePermission(StringView s)
int(s.size), s.data);
}
static unsigned parsePermissions(const char *string)
static unsigned
parsePermissions(std::string_view string)
{
assert(string != nullptr);
unsigned permission = 0;
for (const auto i : IterableSplitString(string, PERMISSION_SEPARATOR))
if (!i.empty())
permission |= ParsePermission(i);
/* for backwards compatiblity with MPD 0.22 and older,
"control" implies "play" */
if (permission & PERMISSION_CONTROL)
permission |= PERMISSION_PLAYER;
return permission;
}
@@ -82,6 +94,7 @@ void
initPermissions(const ConfigData &config)
{
permission_default = PERMISSION_READ | PERMISSION_ADD |
PERMISSION_PLAYER |
PERMISSION_CONTROL | PERMISSION_ADMIN;
for (const auto &param : config.GetParamList(ConfigOption::PASSWORD)) {
@@ -98,8 +111,7 @@ initPermissions(const ConfigData &config)
std::string password(value, separator);
unsigned permission = parsePermissions(separator + 1);
permission_passwords.insert(std::make_pair(std::move(password),
permission));
permission_passwords.emplace(std::move(password), permission);
});
}
@@ -115,17 +127,48 @@ initPermissions(const ConfigData &config)
: permission_default;
});
#endif
#ifdef HAVE_TCP
for (const auto &param : config.GetParamList(ConfigOption::HOST_PERMISSIONS)) {
permission_default = 0;
param.With([](StringView value){
auto [host_sv, permissions_s] = value.Split(' ');
unsigned permissions = parsePermissions(permissions_s);
const std::string host_s{host_sv};
for (const auto &i : Resolve(host_s.c_str(), 0,
AI_PASSIVE, SOCK_STREAM))
host_passwords.emplace(HostToString(i),
permissions);
});
}
#endif
}
#ifdef HAVE_TCP
int
getPermissionFromPassword(const char *password, unsigned *permission) noexcept
GetPermissionsFromAddress(SocketAddress address) noexcept
{
if (auto i = host_passwords.find(HostToString(address));
i != host_passwords.end())
return i->second;
return -1;
}
#endif
std::optional<unsigned>
GetPermissionFromPassword(const char *password) noexcept
{
auto i = permission_passwords.find(password);
if (i == permission_passwords.end())
return -1;
return std::nullopt;
*permission = i->second;
return 0;
return i->second;
}
unsigned

@@ -22,25 +22,42 @@
#include "config.h"
#include <optional>
struct ConfigData;
class SocketAddress;
static constexpr unsigned PERMISSION_NONE = 0;
static constexpr unsigned PERMISSION_READ = 1;
static constexpr unsigned PERMISSION_ADD = 2;
static constexpr unsigned PERMISSION_CONTROL = 4;
static constexpr unsigned PERMISSION_ADMIN = 8;
static constexpr unsigned PERMISSION_PLAYER = 16;
int
getPermissionFromPassword(const char *password, unsigned *permission) noexcept;
/**
* @return the permissions for the given password or std::nullopt if
* the password is not accepted
*/
[[gnu::pure]]
std::optional<unsigned>
GetPermissionFromPassword(const char *password) noexcept;
[[gnu::const]]
unsigned
getDefaultPermissions() noexcept;
#ifdef HAVE_UN
[[gnu::const]]
unsigned
GetLocalPermissions() noexcept;
#endif
#ifdef HAVE_TCP
[[gnu::pure]]
int
GetPermissionsFromAddress(SocketAddress address) noexcept;
#endif
void
initPermissions(const ConfigData &config);

@@ -22,6 +22,7 @@
#include "PlaylistError.hxx"
#include "queue/Playlist.hxx"
#include "queue/QueuePrint.hxx"
#include "protocol/RangeArg.hxx"
#define SONG_FILE "file: "
#define SONG_TIME "Time: "
@@ -35,20 +36,17 @@ playlist_print_uris(Response &r, const playlist &playlist)
}
void
playlist_print_info(Response &r, const playlist &playlist,
unsigned start, unsigned end)
playlist_print_info(Response &r, const playlist &playlist, RangeArg range)
{
const Queue &queue = playlist.queue;
if (end > queue.GetLength())
/* correct the "end" offset */
end = queue.GetLength();
if (start > end)
/* an invalid "start" offset is fatal */
if (!range.CheckClip(queue.GetLength()))
throw PlaylistError::BadRange();
queue_print_info(r, queue, start, end);
if (range.IsEmpty())
return;
queue_print_info(r, queue, range.start, range.end);
}
void
@@ -62,7 +60,7 @@ playlist_print_id(Response &r, const playlist &playlist,
/* no such song */
throw PlaylistError::NoSuchSong();
playlist_print_info(r, playlist, position, position + 1);
playlist_print_info(r, playlist, {unsigned(position), position + 1U});
}
bool
@@ -87,18 +85,24 @@ playlist_print_find(Response &r, const playlist &playlist,
void
playlist_print_changes_info(Response &r, const playlist &playlist,
uint32_t version,
unsigned start, unsigned end)
RangeArg range)
{
queue_print_changes_info(r, playlist.queue, version,
start, end);
const Queue &queue = playlist.queue;
range.ClipRelaxed(queue.GetLength());
queue_print_changes_info(r, queue, version,
range.start, range.end);
}
void
playlist_print_changes_position(Response &r,
const playlist &playlist,
uint32_t version,
unsigned start, unsigned end)
RangeArg range)
{
const Queue &queue = playlist.queue;
range.ClipRelaxed(queue.GetLength());
queue_print_changes_position(r, playlist.queue, version,
start, end);
range.start, range.end);
}

@@ -23,6 +23,7 @@
#include <cstdint>
struct playlist;
struct RangeArg;
class SongFilter;
class Response;
@@ -41,8 +42,7 @@ playlist_print_uris(Response &r, const playlist &playlist);
* Throws #PlaylistError if the range is invalid.
*/
void
playlist_print_info(Response &r, const playlist &playlist,
unsigned start, unsigned end);
playlist_print_info(Response &r, const playlist &playlist, RangeArg range);
/**
* Sends the song with the specified id to the client.
@@ -73,7 +73,7 @@ playlist_print_find(Response &r, const playlist &playlist,
void
playlist_print_changes_info(Response &r, const playlist &playlist,
uint32_t version,
unsigned start, unsigned end);
RangeArg range);
/**
* Print changes since the specified playlist version, position only.
@@ -82,6 +82,6 @@ void
playlist_print_changes_position(Response &r,
const playlist &playlist,
uint32_t version,
unsigned start, unsigned end);
RangeArg range);
#endif

@@ -19,10 +19,14 @@
#include "RemoteTagCache.hxx"
#include "RemoteTagCacheHandler.hxx"
#include "lib/fmt/ExceptionFormatter.hxx"
#include "input/ScanTags.hxx"
#include "util/DeleteDisposer.hxx"
#include "util/Domain.hxx"
#include "Log.hxx"
static constexpr Domain remote_tag_cache_domain("remote_tag_cache");
RemoteTagCache::RemoteTagCache(EventLoop &event_loop,
RemoteTagCacheHandler &_handler) noexcept
:handler(_handler),
@@ -60,9 +64,9 @@ RemoteTagCache::Lookup(const std::string &uri) noexcept
item->scanner->Start();
} catch (...) {
FormatError(std::current_exception(),
"Failed to scan tags of '%s'",
uri.c_str());
FmtError(remote_tag_cache_domain,
"Failed to scan tags of '{}': {}",
uri, std::current_exception());
item->scanner.reset();
@@ -128,7 +132,8 @@ RemoteTagCache::Item::OnRemoteTag(Tag &&_tag) noexcept
void
RemoteTagCache::Item::OnRemoteTagError(std::exception_ptr e) noexcept
{
FormatError(e, "Failed to scan tags of '%s'", uri.c_str());
FmtError(remote_tag_cache_domain,
"Failed to scan tags of '{}': {}", uri, e);
scanner.reset();

@@ -22,7 +22,7 @@
#include "input/RemoteTagScanner.hxx"
#include "tag/Tag.hxx"
#include "event/DeferEvent.hxx"
#include "event/InjectEvent.hxx"
#include "thread/Mutex.hxx"
#include <boost/intrusive/list.hpp>
@@ -40,7 +40,7 @@ class RemoteTagCache final {
RemoteTagCacheHandler &handler;
DeferEvent defer_invoke_handler;
InjectEvent defer_invoke_handler;
Mutex mutex;
@@ -68,20 +68,20 @@ class RemoteTagCache final {
struct Hash : std::hash<std::string> {
using std::hash<std::string>::operator();
gcc_pure
[[gnu::pure]]
std::size_t operator()(const Item &item) const noexcept {
return std::hash<std::string>::operator()(item.uri);
}
};
struct Equal {
gcc_pure
[[gnu::pure]]
bool operator()(const Item &a,
const Item &b) const noexcept {
return a.uri == b.uri;
}
gcc_pure
[[gnu::pure]]
bool operator()(const std::string &a,
const Item &b) const noexcept {
return a == b.uri;

@@ -20,7 +20,6 @@
#ifndef MPD_REPLAY_GAIN_INFO_HXX
#define MPD_REPLAY_GAIN_INFO_HXX
#include "util/Compiler.h"
#include "ReplayGainMode.hxx"
struct ReplayGainConfig;
@@ -42,7 +41,7 @@ struct ReplayGainTuple {
return {-200.0f, 0.0f};
}
gcc_pure
[[gnu::pure]]
float CalculateScale(const ReplayGainConfig &config) const noexcept;
};

@@ -18,6 +18,7 @@
*/
#include "ReplayGainMode.hxx"
#include "util/Compiler.h"
#include <cassert>
#include <stdexcept>

@@ -20,8 +20,6 @@
#ifndef MPD_REPLAY_GAIN_MODE_HXX
#define MPD_REPLAY_GAIN_MODE_HXX
#include "util/Compiler.h"
#include <cstdint>
enum class ReplayGainMode : uint8_t {
@@ -34,7 +32,7 @@ enum class ReplayGainMode : uint8_t {
/**
* Return the string representation of a #ReplayGainMode.
*/
gcc_pure
[[gnu::pure]]
const char *
ToString(ReplayGainMode mode) noexcept;

@@ -18,6 +18,7 @@
*/
#include "SingleMode.hxx"
#include "util/Compiler.h"
#include <cassert>
#include <stdexcept>

@@ -20,8 +20,6 @@
#ifndef MPD_SINGLE_MODE_HXX
#define MPD_SINGLE_MODE_HXX
#include "util/Compiler.h"
#include <cstdint>
enum class SingleMode : uint8_t {
@@ -33,7 +31,7 @@ enum class SingleMode : uint8_t {
/**
* Return the string representation of a #SingleMode.
*/
gcc_pure
[[gnu::pure]]
const char *
SingleToString(SingleMode mode) noexcept;

@@ -20,7 +20,6 @@
#ifndef MPD_SONG_LOADER_HXX
#define MPD_SONG_LOADER_HXX
#include "util/Compiler.h"
#include "config.h"
#include <cstddef>
@@ -72,14 +71,14 @@ public:
/**
* Throws #std::runtime_error on error.
*/
gcc_nonnull_all
[[gnu::nonnull]]
DetachedSong LoadSong(const char *uri_utf8) const;
private:
gcc_nonnull_all
[[gnu::nonnull]]
DetachedSong LoadFromDatabase(const char *uri) const;
gcc_nonnull_all
[[gnu::nonnull]]
DetachedSong LoadFile(const char *path_utf8, Path path_fs) const;
};

@@ -25,8 +25,11 @@
#include "client/Response.hxx"
#include "fs/Traits.hxx"
#include "time/ChronoUtil.hxx"
#include "util/StringBuffer.hxx"
#include "util/UriUtil.hxx"
#include <fmt/format.h>
#define SONG_FILE "file: "
static void
@@ -42,14 +45,15 @@ song_print_uri(Response &r, const char *uri, bool base) noexcept
uri = allocated.c_str();
}
r.Format(SONG_FILE "%s\n", uri);
r.Fmt(FMT_STRING(SONG_FILE "{}\n"), uri);
}
void
song_print_uri(Response &r, const LightSong &song, bool base) noexcept
{
if (!base && song.directory != nullptr)
r.Format(SONG_FILE "%s/%s\n", song.directory, song.uri);
r.Fmt(FMT_STRING(SONG_FILE "{}/{}\n"),
song.directory, song.uri);
else
song_print_uri(r, song.uri, base);
}
@@ -67,15 +71,15 @@ PrintRange(Response &r, SongTime start_time, SongTime end_time) noexcept
const unsigned end_ms = end_time.ToMS();
if (end_ms > 0)
r.Format("Range: %u.%03u-%u.%03u\n",
start_ms / 1000,
start_ms % 1000,
end_ms / 1000,
end_ms % 1000);
r.Fmt(FMT_STRING("Range: {}.{:03}-{}.{:03}\n"),
start_ms / 1000,
start_ms % 1000,
end_ms / 1000,
end_ms % 1000);
else if (start_ms > 0)
r.Format("Range: %u.%03u-\n",
start_ms / 1000,
start_ms % 1000);
r.Fmt(FMT_STRING("Range: {}.{:03}-\n"),
start_ms / 1000,
start_ms % 1000);
}
void
@@ -89,16 +93,16 @@ song_print_info(Response &r, const LightSong &song, bool base) noexcept
time_print(r, "Last-Modified", song.mtime);
if (song.audio_format.IsDefined())
r.Format("Format: %s\n", ToString(song.audio_format).c_str());
r.Fmt(FMT_STRING("Format: {}\n"), ToString(song.audio_format));
tag_print_values(r, song.tag);
const auto duration = song.GetDuration();
if (!duration.IsNegative())
r.Format("Time: %i\n"
"duration: %1.3f\n",
duration.RoundS(),
duration.ToDoubleS());
r.Fmt(FMT_STRING("Time: {}\n"
"duration: {:1.3f}\n"),
duration.RoundS(),
duration.ToDoubleS());
}
void
@@ -111,12 +115,15 @@ song_print_info(Response &r, const DetachedSong &song, bool base) noexcept
if (!IsNegative(song.GetLastModified()))
time_print(r, "Last-Modified", song.GetLastModified());
if (const auto &f = song.GetAudioFormat(); f.IsDefined())
r.Fmt(FMT_STRING("Format: {}\n"), ToString(f));
tag_print_values(r, song.GetTag());
const auto duration = song.GetDuration();
if (!duration.IsNegative())
r.Format("Time: %i\n"
"duration: %1.3f\n",
duration.RoundS(),
duration.ToDoubleS());
r.Fmt(FMT_STRING("Time: {}\n"
"duration: {:1.3f}\n"),
duration.RoundS(),
duration.ToDoubleS());
}

@@ -86,8 +86,7 @@ song_save(BufferedOutputStream &os, const DetachedSong &song)
DetachedSong
song_load(TextFile &file, const char *uri,
std::string *target_r,
AudioFormat *audio_format_r)
std::string *target_r)
{
DetachedSong song(uri);
@@ -113,13 +112,11 @@ song_load(TextFile &file, const char *uri,
if (target_r != nullptr)
*target_r = value;
} else if (StringIsEqual(line, "Format")) {
if (audio_format_r != nullptr) {
try {
*audio_format_r =
ParseAudioFormat(value, false);
} catch (...) {
/* ignore parser errors */
}
try {
song.SetAudioFormat(ParseAudioFormat(value,
false));
} catch (...) {
/* ignore parser errors */
}
} else if (StringIsEqual(line, "Playlist")) {
tag.SetHasPlaylist(StringIsEqual(value, "yes"));

@@ -44,7 +44,6 @@ song_save(BufferedOutputStream &os, const DetachedSong &song);
*/
DetachedSong
song_load(TextFile &file, const char *uri,
std::string *target_r=nullptr,
AudioFormat *audio_format_r=nullptr);
std::string *target_r=nullptr);
#endif

@@ -153,9 +153,10 @@ DetachedSong::LoadFile(Path path)
return false;
TagBuilder tag_builder;
auto new_audio_format = AudioFormat::Undefined();
try {
if (!ScanFileTagsWithGeneric(path, tag_builder))
if (!ScanFileTagsWithGeneric(path, tag_builder, &new_audio_format))
return false;
} catch (...) {
// TODO: log or propagate I/O errors?
@@ -163,6 +164,7 @@ DetachedSong::LoadFile(Path path)
}
mtime = fi.GetModificationTime();
audio_format = new_audio_format;
tag_builder.Commit(tag);
return true;
}
@@ -177,9 +179,11 @@ DetachedSong::Update()
return LoadFile(path_fs);
} else if (IsRemote()) {
TagBuilder tag_builder;
auto new_audio_format = AudioFormat::Undefined();
try {
if (!tag_stream_scan(uri.c_str(), tag_builder))
if (!tag_stream_scan(uri.c_str(), tag_builder,
&new_audio_format))
return false;
} catch (...) {
// TODO: log or propagate I/O errors?
@@ -187,6 +191,7 @@ DetachedSong::Update()
}
mtime = std::chrono::system_clock::time_point::min();
audio_format = new_audio_format;
tag_builder.Commit(tag);
return true;
} else

@@ -93,8 +93,8 @@ StateFile::Write(OutputStream &os)
void
StateFile::Write()
{
FormatDebug(state_file_domain,
"Saving state file %s", path_utf8.c_str());
FmtDebug(state_file_domain,
"Saving state file {}", path_utf8);
try {
FileOutputStream fos(config.path);
@@ -112,7 +112,7 @@ StateFile::Read()
try {
bool success;
FormatDebug(state_file_domain, "Loading state file %s", path_utf8.c_str());
FmtDebug(state_file_domain, "Loading state file {}", path_utf8);
TextFile file(config.path);
@@ -135,9 +135,9 @@ try {
#endif
if (!success)
FormatError(state_file_domain,
"Unrecognized line in state file: %s",
line);
FmtError(state_file_domain,
"Unrecognized line in state file: {}",
line);
}
RememberVersions();
@@ -148,7 +148,7 @@ try {
void
StateFile::CheckModified() noexcept
{
if (!timer_event.IsActive() && IsModified())
if (!timer_event.IsPending() && IsModified())
timer_event.Schedule(config.interval);
}

@@ -21,8 +21,7 @@
#define MPD_STATE_FILE_HXX
#include "StateFileConfig.hxx"
#include "event/TimerEvent.hxx"
#include "util/Compiler.h"
#include "event/FarTimerEvent.hxx"
#include "config.h"
#include <string>
@@ -36,7 +35,7 @@ class StateFile final {
const std::string path_utf8;
TimerEvent timer_event;
FarTimerEvent timer_event;
Partition &partition;
@@ -76,7 +75,7 @@ private:
* Check if MPD's state was modified since the last
* RememberVersions() call.
*/
gcc_pure
[[gnu::pure]]
bool IsModified() const noexcept;
/* callback for #timer_event */

@@ -34,6 +34,8 @@
#include "system/Clock.hxx"
#endif
#include <fmt/format.h>
#include <chrono>
#ifndef _WIN32
@@ -97,19 +99,19 @@ db_stats_print(Response &r, const Database &db)
unsigned total_duration_s =
std::chrono::duration_cast<std::chrono::seconds>(stats.total_duration).count();
r.Format("artists: %u\n"
"albums: %u\n"
"songs: %u\n"
"db_playtime: %u\n",
stats.artist_count,
stats.album_count,
stats.song_count,
total_duration_s);
r.Fmt(FMT_STRING("artists: {}\n"
"albums: {}\n"
"songs: {}\n"
"db_playtime: {}\n"),
stats.artist_count,
stats.album_count,
stats.song_count,
total_duration_s);
const auto update_stamp = db.GetUpdateStamp();
if (!IsNegative(update_stamp))
r.Format("db_update: %lu\n",
(unsigned long)std::chrono::system_clock::to_time_t(update_stamp));
r.Fmt(FMT_STRING("db_update: {}\n"),
std::chrono::system_clock::to_time_t(update_stamp));
}
#endif
@@ -123,10 +125,10 @@ stats_print(Response &r, const Partition &partition)
const auto uptime = std::chrono::steady_clock::now() - start_time;
#endif
r.Format("uptime: %u\n"
"playtime: %lu\n",
(unsigned)std::chrono::duration_cast<std::chrono::seconds>(uptime).count(),
lround(partition.pc.GetTotalPlayTime().count()));
r.Fmt(FMT_STRING("uptime: {}\n"
"playtime: {}\n"),
std::chrono::duration_cast<std::chrono::seconds>(uptime).count(),
lround(partition.pc.GetTotalPlayTime().count()));
#ifdef ENABLE_DATABASE
const Database *db = partition.instance.GetDatabase();

@@ -25,6 +25,7 @@
#include "client/Client.hxx"
#include "protocol/Ack.hxx"
#include "fs/AllocatedPath.hxx"
#include "input/InputStream.hxx"
#include "util/Compiler.h"
#include "util/UriExtract.hxx"
#include "LocateUri.hxx"
@@ -32,8 +33,13 @@
static void
TagScanStream(const char *uri, TagHandler &handler)
{
if (!tag_stream_scan(uri, handler))
Mutex mutex;
auto is = InputStream::OpenReady(uri, mutex);
if (!tag_stream_scan(*is, handler))
throw ProtocolError(ACK_ERROR_NO_EXIST, "Failed to load file");
ScanGenericTags(*is, handler);
}
static void

@@ -23,26 +23,27 @@
#include "client/Response.hxx"
#include "util/StringView.hxx"
#include <fmt/format.h>
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 (tag_mask.Test(TagType(i)))
r.Format("tagtype: %s\n", tag_item_names[i]);
r.Fmt(FMT_STRING("tagtype: {}\n"), tag_item_names[i]);
}
void
tag_print(Response &r, TagType type, StringView value) noexcept
{
r.Format("%s: %.*s\n", tag_item_names[type],
int(value.size), value.data);
r.Fmt(FMT_STRING("{}: {}\n"), tag_item_names[type], value);
}
void
tag_print(Response &r, TagType type, const char *value) noexcept
{
r.Format("%s: %s\n", tag_item_names[type], value);
r.Fmt(FMT_STRING("{}: {}\n"), tag_item_names[type], value);
}
void
@@ -58,10 +59,10 @@ void
tag_print(Response &r, const Tag &tag) noexcept
{
if (!tag.duration.IsNegative())
r.Format("Time: %i\n"
"duration: %1.3f\n",
tag.duration.RoundS(),
tag.duration.ToDoubleS());
r.Fmt(FMT_STRING("Time: {}\n"
"duration: {:1.3f}\n"),
tag.duration.RoundS(),
tag.duration.ToDoubleS());
tag_print_values(r, tag);
}

@@ -36,10 +36,10 @@
gcc_pure
static bool
CheckDecoderPlugin(const DecoderPlugin &plugin,
const char *suffix, const char *mime) noexcept
std::string_view suffix, std::string_view mime) noexcept
{
return (mime != nullptr && plugin.SupportsMimeType(mime)) ||
(suffix != nullptr && plugin.SupportsSuffix(suffix));
return (!mime.empty() && plugin.SupportsMimeType(mime)) ||
(!suffix.empty() && plugin.SupportsSuffix(suffix));
}
bool
@@ -47,25 +47,24 @@ tag_stream_scan(InputStream &is, TagHandler &handler)
{
assert(is.IsReady());
UriSuffixBuffer suffix_buffer;
const char *const suffix = uri_get_suffix(is.GetURI(), suffix_buffer);
const char *mime = is.GetMimeType();
const auto suffix = uri_get_suffix(is.GetURI());
const char *full_mime = is.GetMimeType();
if (suffix == nullptr && mime == nullptr)
if (suffix.empty() && full_mime == nullptr)
return false;
std::string mime_base;
if (mime != nullptr)
mime = (mime_base = GetMimeTypeBase(mime)).c_str();
std::string_view mime_base{};
if (full_mime != nullptr)
mime_base = GetMimeTypeBase(full_mime);
return decoder_plugins_try([suffix, mime, &is,
return decoder_plugins_try([suffix, mime_base, &is,
&handler](const DecoderPlugin &plugin){
try {
is.LockRewind();
} catch (...) {
}
return CheckDecoderPlugin(plugin, suffix, mime) &&
return CheckDecoderPlugin(plugin, suffix, mime_base) &&
plugin.ScanStream(is, handler);
});
}

@@ -20,6 +20,9 @@
#include "TimePrint.hxx"
#include "client/Response.hxx"
#include "time/ISO8601.hxx"
#include "util/StringBuffer.hxx"
#include <fmt/format.h>
void
time_print(Response &r, const char *name,
@@ -33,5 +36,5 @@ time_print(Response &r, const char *name,
return;
}
r.Format("%s: %s\n", name, s.c_str());
r.Fmt(FMT_STRING("{}: {}\n"), name, s);
}

@@ -30,10 +30,6 @@ class AudioManager : public Java::GlobalObject {
public:
AudioManager(JNIEnv *env, jobject obj) noexcept;
AudioManager(std::nullptr_t) noexcept { maxVolume = 0; }
~AudioManager() noexcept {}
int GetMaxVolume() { return maxVolume; }
int GetVolume(JNIEnv *env);
void SetVolume(JNIEnv *env, int);

@@ -26,6 +26,25 @@
#include "AudioManager.hxx"
AllocatedPath
Context::GetExternalFilesDir(JNIEnv *env, const char *_type) noexcept
{
assert(_type != nullptr);
Java::Class cls{env, env->GetObjectClass(Get())};
jmethodID method = env->GetMethodID(cls, "getExternalFilesDir",
"(Ljava/lang/String;)Ljava/io/File;");
assert(method);
Java::String type{env, _type};
jobject file = env->CallObjectMethod(Get(), method, type.Get());
if (Java::DiscardException(env) || file == nullptr)
return nullptr;
return Java::File::ToAbsolutePath(env, file);
}
AllocatedPath
Context::GetCacheDir(JNIEnv *env) const noexcept
{

@@ -30,10 +30,14 @@ public:
Context(JNIEnv *env, jobject obj) noexcept
:Java::GlobalObject(env, obj) {}
gcc_pure
[[gnu::pure]]
AllocatedPath GetExternalFilesDir(JNIEnv *env,
const char *type) noexcept;
[[gnu::pure]]
AllocatedPath GetCacheDir(JNIEnv *env) const noexcept;
gcc_pure
[[gnu::pure]]
AudioManager *GetAudioManager(JNIEnv *env) noexcept;
};

@@ -33,10 +33,10 @@ namespace Environment {
/**
* Determine the mount point of the external SD card.
*/
gcc_pure
[[gnu::pure]]
AllocatedPath getExternalStorageDirectory() noexcept;
gcc_pure
[[gnu::pure]]
AllocatedPath getExternalStoragePublicDirectory(const char *type) noexcept;
}

@@ -20,27 +20,21 @@
#include "LogListener.hxx"
#include "java/Class.hxx"
#include "java/String.hxx"
#include "util/AllocatedString.hxx"
#include "util/FormatString.hxx"
LogListener::LogListener(JNIEnv *env, jobject obj) noexcept
:Java::GlobalObject(env, obj)
{
Java::Class cls(env, env->GetObjectClass(Get()));
onLogMethod = env->GetMethodID(cls, "onLog", "(ILjava/lang/String;)V");
assert(onLogMethod);
}
void
LogListener::OnLog(JNIEnv *env, int priority,
const char *fmt, ...) const noexcept
LogListener::OnLog(JNIEnv *env, int priority, const char *msg) const noexcept
{
assert(env != nullptr);
Java::Class cls(env, env->GetObjectClass(Get()));
jmethodID method = env->GetMethodID(cls, "onLog",
"(ILjava/lang/String;)V");
assert(method);
std::va_list args;
va_start(args, fmt);
const auto log = FormatStringV(fmt, args);
va_end(args);
env->CallVoidMethod(Get(), method, priority,
Java::String(env, log.c_str()).Get());
env->CallVoidMethod(Get(), onLogMethod, priority,
Java::String(env, msg).Get());
}

@@ -23,12 +23,12 @@
#include "java/Object.hxx"
class LogListener : public Java::GlobalObject {
public:
LogListener(JNIEnv *env, jobject obj) noexcept
:Java::GlobalObject(env, obj) {}
jmethodID onLogMethod;
void OnLog(JNIEnv *env, int priority,
const char *fmt, ...) const noexcept;
public:
LogListener(JNIEnv *env, jobject obj) noexcept;
void OnLog(JNIEnv *env, int priority, const char *msg) const noexcept;
};
#endif

@@ -30,7 +30,7 @@
#include <string.h>
const ArchivePlugin *const archive_plugins[] = {
constexpr const ArchivePlugin *archive_plugins[] = {
#ifdef ENABLE_BZ2
&bz2_archive_plugin,
#endif
@@ -55,10 +55,8 @@ static bool archive_plugins_enabled[std::max(n_archive_plugins, std::size_t(1))]
if (archive_plugins_enabled[archive_plugin_iterator - archive_plugins])
const ArchivePlugin *
archive_plugin_from_suffix(const char *suffix) noexcept
archive_plugin_from_suffix(std::string_view suffix) noexcept
{
assert(suffix != nullptr);
archive_plugins_for_each_enabled(plugin)
if (plugin->suffixes != nullptr &&
StringArrayContainsCase(plugin->suffixes, suffix))

@@ -20,6 +20,8 @@
#ifndef MPD_ARCHIVE_LIST_HXX
#define MPD_ARCHIVE_LIST_HXX
#include <string_view>
struct ArchivePlugin;
extern const ArchivePlugin *const archive_plugins[];
@@ -33,7 +35,7 @@ extern const ArchivePlugin *const archive_plugins[];
/* interface for using plugins */
const ArchivePlugin *
archive_plugin_from_suffix(const char *suffix) noexcept;
archive_plugin_from_suffix(std::string_view suffix) noexcept;
const ArchivePlugin *
archive_plugin_from_name(const char *name) noexcept;

@@ -25,6 +25,9 @@ archive_glue = static_library(
'ArchivePlugin.cxx',
'../input/plugins/ArchiveInputPlugin.cxx',
include_directories: inc,
dependencies: [
log_dep,
],
)
archive_glue_dep = declare_dependency(

@@ -71,6 +71,9 @@ public:
Mutex &mutex);
~Bzip2InputStream() noexcept override;
Bzip2InputStream(const Bzip2InputStream &) = delete;
Bzip2InputStream &operator=(const Bzip2InputStream &) = delete;
/* virtual methods from InputStream */
[[nodiscard]] bool IsEOF() const noexcept override;
size_t Read(std::unique_lock<Mutex> &lock,
@@ -177,7 +180,7 @@ Bzip2InputStream::IsEOF() const noexcept
/* exported structures */
static const char *const bz2_extensions[] = {
static constexpr const char *bz2_extensions[] = {
"bz2",
nullptr
};

@@ -29,6 +29,7 @@
#include "fs/Path.hxx"
#include "util/RuntimeError.hxx"
#include "util/StringCompare.hxx"
#include "util/UTF8.hxx"
#include "util/WritableBuffer.hxx"
#include <cdio/iso9660.h>
@@ -102,6 +103,10 @@ Iso9660ArchiveFile::Visit(char *path, size_t length, size_t capacity,
/* skip special names like "." and ".." */
continue;
if (!ValidateUTF8(filename))
/* ignore file names which are not valid UTF-8 */
continue;
size_t filename_length = strlen(filename);
if (length + filename_length + 1 >= capacity)
/* file name is too long */
@@ -319,7 +324,7 @@ Iso9660InputStream::IsEOF() const noexcept
/* exported structures */
static const char *const iso9660_archive_extensions[] = {
static constexpr const char * iso9660_archive_extensions[] = {
"iso",
nullptr
};

@@ -29,6 +29,7 @@
#include "fs/Path.hxx"
#include "system/Error.hxx"
#include "util/RuntimeError.hxx"
#include "util/UTF8.hxx"
#include <zzip/zzip.h>
@@ -84,7 +85,7 @@ ZzipArchiveFile::Visit(ArchiveVisitor &visitor)
ZZIP_DIRENT dirent;
while (zzip_dir_read(dir->dir, &dirent))
//add only files
if (dirent.st_size > 0)
if (dirent.st_size > 0 && ValidateUTF8(dirent.d_name))
visitor.VisitArchiveEntry(dirent.d_name);
}
@@ -116,6 +117,9 @@ public:
zzip_file_close(file);
}
ZzipInputStream(const ZzipInputStream &) = delete;
ZzipInputStream &operator=(const ZzipInputStream &) = delete;
/* virtual methods from InputStream */
[[nodiscard]] bool IsEOF() const noexcept override;
size_t Read(std::unique_lock<Mutex> &lock,
@@ -185,7 +189,7 @@ ZzipInputStream::Seek(std::unique_lock<Mutex> &, offset_type new_offset)
/* exported structures */
static const char *const zzip_archive_extensions[] = {
static constexpr const char *zzip_archive_extensions[] = {
"zip",
nullptr
};

@@ -26,8 +26,7 @@
#include "input/LastInputStream.hxx"
#include "tag/Mask.hxx"
#include "event/FullyBufferedSocket.hxx"
#include "event/TimerEvent.hxx"
#include "util/Compiler.h"
#include "event/CoarseTimerEvent.hxx"
#include <boost/intrusive/link_mode.hpp>
#include <boost/intrusive/list_hook.hpp>
@@ -55,7 +54,7 @@ class Client final
public boost::intrusive::list_base_hook<boost::intrusive::tag<Partition>,
boost::intrusive::link_mode<boost::intrusive::normal_link>>,
public boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>> {
TimerEvent timeout_event;
CoarseTimerEvent timeout_event;
Partition *partition;
@@ -138,7 +137,7 @@ public:
using FullyBufferedSocket::GetEventLoop;
using FullyBufferedSocket::GetOutputMaxSize;
gcc_pure
[[gnu::pure]]
bool IsExpired() const noexcept {
return !FullyBufferedSocket::IsDefined();
}
@@ -211,7 +210,7 @@ public:
FULL,
};
gcc_pure
[[gnu::pure]]
bool IsSubscribed(const char *channel_name) const noexcept {
return subscriptions.find(channel_name) != subscriptions.end();
}
@@ -252,19 +251,19 @@ public:
void SetPartition(Partition &new_partition) noexcept;
gcc_pure
[[gnu::pure]]
Instance &GetInstance() const noexcept;
gcc_pure
[[gnu::pure]]
playlist &GetPlaylist() const noexcept;
gcc_pure
[[gnu::pure]]
PlayerControl &GetPlayerControl() const noexcept;
/**
* Wrapper for Instance::GetDatabase().
*/
gcc_pure
[[gnu::pure]]
const Database *GetDatabase() const noexcept;
/**
@@ -272,7 +271,7 @@ public:
*/
const Database &GetDatabaseOrThrow() const;
gcc_pure
[[gnu::pure]]
const Storage *GetStorage() const noexcept;
private:

@@ -18,12 +18,14 @@
*/
#include "Client.hxx"
#include "Domain.hxx"
#include "lib/fmt/ExceptionFormatter.hxx"
#include "Log.hxx"
void
Client::OnSocketError(std::exception_ptr ep) noexcept
{
FormatError(ep, "error on client %d", num);
FmtError(client_domain, "error on client {}: {}", num, ep);
SetExpired();
}

@@ -44,7 +44,7 @@ Client::OnTimeout() noexcept
assert(!idle_waiting);
assert(!background_command);
FormatDebug(client_domain, "[%u] timeout", num);
FmtDebug(client_domain, "[{}] timeout", num);
}
Close();

@@ -22,6 +22,8 @@
#include "Response.hxx"
#include "Idle.hxx"
#include <fmt/format.h>
#include <cassert>
static void
@@ -30,7 +32,7 @@ WriteIdleResponse(Response &r, unsigned flags) noexcept
const char *const*idle_names = idle_get_names();
for (unsigned i = 0; idle_names[i]; ++i) {
if (flags & (1 << i))
r.Format("changed: %s\n", idle_names[i]);
r.Fmt(FMT_STRING("changed: {}\n"), idle_names[i]);
}
r.Write("OK\n");

@@ -32,8 +32,12 @@ GetPermissions(SocketAddress address, int uid) noexcept
#ifdef HAVE_UN
if (address.GetFamily() == AF_LOCAL)
return GetLocalPermissions();
#else
(void)address;
#endif
#ifdef HAVE_TCP
if (int permissions = GetPermissionsFromAddress(address);
permissions >= 0)
return permissions;
#endif
return getDefaultPermissions();

@@ -20,8 +20,6 @@
#ifndef MPD_CLIENT_MESSAGE_HXX
#define MPD_CLIENT_MESSAGE_HXX
#include "util/Compiler.h"
#include <string>
#ifdef _WIN32
@@ -51,7 +49,7 @@ public:
}
};
gcc_pure
[[gnu::pure]]
bool
client_message_valid_channel_name(const char *name) noexcept;

@@ -76,8 +76,8 @@ client_new(EventLoop &loop, Partition &partition,
client_list.Add(*client);
partition.clients.push_back(*client);
FormatInfo(client_domain, "[%u] opened from %s",
num, remote.c_str());
FmtInfo(client_domain, "[{}] opened from {}",
num, remote);
}
void
@@ -89,6 +89,6 @@ Client::Close() noexcept
if (FullyBufferedSocket::IsDefined())
FullyBufferedSocket::Close();
FormatInfo(client_domain, "[%u] closed", num);
FmtInfo(client_domain, "[{}] closed", num);
delete this;
}

@@ -39,9 +39,9 @@ Client::ProcessCommandList(bool list_ok,
for (auto &&i : list) {
char *cmd = &*i.begin();
FormatDebug(client_domain, "process command \"%s\"", cmd);
FmtDebug(client_domain, "process command \"{}\"", cmd);
auto ret = command_process(*this, n++, cmd);
FormatDebug(client_domain, "command returned %i", int(ret));
FmtDebug(client_domain, "command returned {}", unsigned(ret));
if (IsExpired())
return CommandResult::CLOSE;
else if (ret != CommandResult::OK)
@@ -62,9 +62,9 @@ Client::ProcessLine(char *line) noexcept
/* all valid MPD commands begin with a lower case
letter; this could be a badly routed HTTP
request */
FormatWarning(client_domain,
"[%u] malformed command \"%s\"",
num, line);
FmtWarning(client_domain,
"[{}] malformed command \"{}\"",
num, line);
return CommandResult::CLOSE;
}
@@ -83,9 +83,9 @@ Client::ProcessLine(char *line) noexcept
} else if (idle_waiting) {
/* during idle mode, clients must not send anything
except "noidle" */
FormatWarning(client_domain,
"[%u] command \"%s\" during idle",
num, line);
FmtWarning(client_domain,
"[{}] command \"{}\" during idle",
num, line);
return CommandResult::CLOSE;
}
@@ -93,9 +93,9 @@ Client::ProcessLine(char *line) noexcept
if (StringIsEqual(line, CLIENT_LIST_MODE_END)) {
const unsigned id = num;
FormatDebug(client_domain,
"[%u] process command list",
id);
FmtDebug(client_domain,
"[{}] process command list",
id);
const bool ok_mode = cmd_list.IsOKMode();
auto list = cmd_list.Commit();
@@ -103,9 +103,9 @@ Client::ProcessLine(char *line) noexcept
auto ret = ProcessCommandList(ok_mode,
std::move(list));
FormatDebug(client_domain,
"[%u] process command "
"list returned %i", id, int(ret));
FmtDebug(client_domain,
"[{}] process command "
"list returned {}", id, unsigned(ret));
if (ret == CommandResult::OK)
command_success(*this);
@@ -113,11 +113,10 @@ Client::ProcessLine(char *line) noexcept
return ret;
} else {
if (!cmd_list.Add(line)) {
FormatWarning(client_domain,
"[%u] command list size "
"is larger than the max (%lu)",
num,
(unsigned long)client_max_command_list_size);
FmtWarning(client_domain,
"[{}] command list size "
"is larger than the max ({})",
num, client_max_command_list_size);
return CommandResult::CLOSE;
}
@@ -133,13 +132,13 @@ Client::ProcessLine(char *line) noexcept
} else {
const unsigned id = num;
FormatDebug(client_domain,
"[%u] process command \"%s\"",
id, line);
FmtDebug(client_domain,
"[{}] process command \"{}\"",
id, line);
auto ret = command_process(*this, 0, line);
FormatDebug(client_domain,
"[%u] command returned %i",
id, int(ret));
FmtDebug(client_domain,
"[{}] command returned {}",
id, unsigned(ret));
if (IsExpired())
return CommandResult::CLOSE;

@@ -19,8 +19,8 @@
#include "Response.hxx"
#include "Client.hxx"
#include "util/FormatString.hxx"
#include "util/AllocatedString.hxx"
#include <fmt/format.h>
TagMask
Response::GetTagMask() const noexcept
@@ -41,19 +41,15 @@ Response::Write(const char *data) noexcept
}
bool
Response::FormatV(const char *fmt, std::va_list args) noexcept
Response::VFmt(fmt::string_view format_str, fmt::format_args args) noexcept
{
return Write(FormatStringV(fmt, args).c_str());
}
bool
Response::Format(const char *fmt, ...) noexcept
{
std::va_list args;
va_start(args, fmt);
bool success = FormatV(fmt, args);
va_end(args);
return success;
fmt::memory_buffer buffer;
#if FMT_VERSION >= 80000
fmt::vformat_to(std::back_inserter(buffer), format_str, args);
#else
fmt::vformat_to(buffer, format_str, args);
#endif
return Write(buffer.data(), buffer.size());
}
bool
@@ -62,11 +58,7 @@ Response::WriteBinary(ConstBuffer<void> payload) noexcept
assert(payload.size <= client.binary_limit);
return
#ifdef _WIN32
Format("binary: %lu\n", (unsigned long)payload.size) &&
#else
Format("binary: %zu\n", payload.size) &&
#endif
Fmt("binary: {}\n", payload.size) &&
Write(payload.data, payload.size) &&
Write("\n");
}
@@ -74,19 +66,17 @@ Response::WriteBinary(ConstBuffer<void> payload) noexcept
void
Response::Error(enum ack code, const char *msg) noexcept
{
FormatError(code, "%s", msg);
FmtError(code, FMT_STRING("{}"), msg);
}
void
Response::FormatError(enum ack code, const char *fmt, ...) noexcept
Response::VFmtError(enum ack code,
fmt::string_view format_str, fmt::format_args args) noexcept
{
Format("ACK [%i@%u] {%s} ",
(int)code, list_index, command);
Fmt(FMT_STRING("ACK [{}@{}] {{{}}} "),
(int)code, list_index, command);
std::va_list args;
va_start(args, fmt);
FormatV(fmt, args);
va_end(args);
VFmt(format_str, std::move(args));
Write("\n");
}

@@ -21,9 +21,12 @@
#define MPD_RESPONSE_HXX
#include "protocol/Ack.hxx"
#include "util/Compiler.h"
#include <cstdarg>
#include <fmt/core.h>
#if FMT_VERSION < 70000 || FMT_VERSION >= 80000
#include <fmt/format.h>
#endif
#include <cstddef>
template<typename T> struct ConstBuffer;
@@ -65,7 +68,7 @@ public:
* Accessor for Client::tag_mask. Can be used if caller wants
* to avoid including Client.hxx.
*/
gcc_pure
[[gnu::pure]]
TagMask GetTagMask() const noexcept;
void SetCommand(const char *_command) noexcept {
@@ -74,10 +77,21 @@ public:
bool Write(const void *data, size_t length) noexcept;
bool Write(const char *data) noexcept;
bool FormatV(const char *fmt, std::va_list args) noexcept;
gcc_printf(2,3)
bool Format(const char *fmt, ...) noexcept;
bool VFmt(fmt::string_view format_str, fmt::format_args args) noexcept;
template<typename S, typename... Args>
bool Fmt(const S &format_str, Args&&... args) noexcept {
#if FMT_VERSION >= 70000
return VFmt(fmt::to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str,
args...));
#else
/* expensive fallback for older libfmt versions */
const auto result = fmt::format(format_str, args...);
return Write(result.data(), result.size());
#endif
}
/**
* Write a binary chunk; this writes the "binary" line, the
@@ -88,7 +102,23 @@ public:
bool WriteBinary(ConstBuffer<void> payload) noexcept;
void Error(enum ack code, const char *msg) noexcept;
void FormatError(enum ack code, const char *fmt, ...) noexcept;
void VFmtError(enum ack code,
fmt::string_view format_str, fmt::format_args args) noexcept;
template<typename S, typename... Args>
void FmtError(enum ack code,
const S &format_str, Args&&... args) noexcept {
#if FMT_VERSION >= 70000
return VFmtError(code, fmt::to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str,
args...));
#else
/* expensive fallback for older libfmt versions */
const auto result = fmt::format(format_str, args...);
return Error(code, result.c_str());
#endif
}
};
#endif

@@ -70,7 +70,7 @@ ThreadBackgroundCommand::Cancel() noexcept
CancelThread();
thread.Join();
/* cancel the DeferEvent, just in case the Thread has
/* cancel the InjectEvent, just in case the Thread has
meanwhile finished execution */
defer_finish.Cancel();
}

@@ -21,7 +21,7 @@
#define MPD_THREAD_BACKGROUND_COMMAND_HXX
#include "BackgroundCommand.hxx"
#include "event/DeferEvent.hxx"
#include "event/InjectEvent.hxx"
#include "thread/Thread.hxx"
#include <exception>
@@ -34,7 +34,7 @@ class Response;
*/
class ThreadBackgroundCommand : public BackgroundCommand {
Thread thread;
DeferEvent defer_finish;
InjectEvent defer_finish;
Client &client;
/**

@@ -48,6 +48,8 @@
#include "StickerCommands.hxx"
#endif
#include <fmt/format.h>
#include <cassert>
#include <iterator>
@@ -89,21 +91,21 @@ static constexpr struct command commands[] = {
{ "albumart", PERMISSION_READ, 2, 2, handle_album_art },
{ "binarylimit", PERMISSION_NONE, 1, 1, handle_binary_limit },
{ "channels", PERMISSION_READ, 0, 0, handle_channels },
{ "clear", PERMISSION_CONTROL, 0, 0, handle_clear },
{ "clearerror", PERMISSION_CONTROL, 0, 0, handle_clearerror },
{ "clear", PERMISSION_PLAYER, 0, 0, handle_clear },
{ "clearerror", PERMISSION_PLAYER, 0, 0, handle_clearerror },
{ "cleartagid", PERMISSION_ADD, 1, 2, handle_cleartagid },
{ "close", PERMISSION_NONE, -1, -1, handle_close },
{ "commands", PERMISSION_NONE, 0, 0, handle_commands },
{ "config", PERMISSION_ADMIN, 0, 0, handle_config },
{ "consume", PERMISSION_CONTROL, 1, 1, handle_consume },
{ "consume", PERMISSION_PLAYER, 1, 1, handle_consume },
#ifdef ENABLE_DATABASE
{ "count", PERMISSION_READ, 1, -1, handle_count },
#endif
{ "crossfade", PERMISSION_CONTROL, 1, 1, handle_crossfade },
{ "crossfade", PERMISSION_PLAYER, 1, 1, handle_crossfade },
{ "currentsong", PERMISSION_READ, 0, 0, handle_currentsong },
{ "decoders", PERMISSION_READ, 0, 0, handle_decoders },
{ "delete", PERMISSION_CONTROL, 1, 1, handle_delete },
{ "deleteid", PERMISSION_CONTROL, 1, 1, handle_deleteid },
{ "delete", PERMISSION_PLAYER, 1, 1, handle_delete },
{ "deleteid", PERMISSION_PLAYER, 1, 1, handle_deleteid },
{ "delpartition", PERMISSION_ADMIN, 1, 1, handle_delpartition },
{ "disableoutput", PERMISSION_ADMIN, 1, 1, handle_disableoutput },
{ "enableoutput", PERMISSION_ADMIN, 1, 1, handle_enableoutput },
@@ -114,6 +116,7 @@ static constexpr struct command commands[] = {
#ifdef ENABLE_CHROMAPRINT
{ "getfingerprint", PERMISSION_READ, 1, 1, handle_getfingerprint },
#endif
{ "getvol", PERMISSION_READ, 0, 0, handle_getvol },
{ "idle", PERMISSION_READ, 0, -1, handle_idle },
{ "kill", PERMISSION_ADMIN, -1, -1, handle_kill },
#ifdef ENABLE_DATABASE
@@ -132,27 +135,27 @@ static constexpr struct command commands[] = {
{ "listplaylist", PERMISSION_READ, 1, 1, handle_listplaylist },
{ "listplaylistinfo", PERMISSION_READ, 1, 1, handle_listplaylistinfo },
{ "listplaylists", PERMISSION_READ, 0, 0, handle_listplaylists },
{ "load", PERMISSION_ADD, 1, 2, handle_load },
{ "load", PERMISSION_ADD, 1, 3, handle_load },
{ "lsinfo", PERMISSION_READ, 0, 1, handle_lsinfo },
{ "mixrampdb", PERMISSION_CONTROL, 1, 1, handle_mixrampdb },
{ "mixrampdelay", PERMISSION_CONTROL, 1, 1, handle_mixrampdelay },
{ "mixrampdb", PERMISSION_PLAYER, 1, 1, handle_mixrampdb },
{ "mixrampdelay", PERMISSION_PLAYER, 1, 1, handle_mixrampdelay },
#ifdef ENABLE_DATABASE
{ "mount", PERMISSION_ADMIN, 2, 2, handle_mount },
#endif
{ "move", PERMISSION_CONTROL, 2, 2, handle_move },
{ "moveid", PERMISSION_CONTROL, 2, 2, handle_moveid },
{ "move", PERMISSION_PLAYER, 2, 2, handle_move },
{ "moveid", PERMISSION_PLAYER, 2, 2, handle_moveid },
{ "moveoutput", PERMISSION_ADMIN, 1, 1, handle_moveoutput },
{ "newpartition", PERMISSION_ADMIN, 1, 1, handle_newpartition },
{ "next", PERMISSION_CONTROL, 0, 0, handle_next },
{ "next", PERMISSION_PLAYER, 0, 0, handle_next },
{ "notcommands", PERMISSION_NONE, 0, 0, handle_not_commands },
{ "outputs", PERMISSION_READ, 0, 0, handle_devices },
{ "outputset", PERMISSION_ADMIN, 3, 3, handle_outputset },
{ "partition", PERMISSION_READ, 1, 1, handle_partition },
{ "password", PERMISSION_NONE, 1, 1, handle_password },
{ "pause", PERMISSION_CONTROL, 0, 1, handle_pause },
{ "pause", PERMISSION_PLAYER, 0, 1, handle_pause },
{ "ping", PERMISSION_NONE, 0, 0, handle_ping },
{ "play", PERMISSION_CONTROL, 0, 1, handle_play },
{ "playid", PERMISSION_CONTROL, 0, 1, handle_playid },
{ "play", PERMISSION_PLAYER, 0, 1, handle_play },
{ "playid", PERMISSION_PLAYER, 0, 1, handle_playid },
{ "playlist", PERMISSION_READ, 0, 0, handle_playlist },
{ "playlistadd", PERMISSION_CONTROL, 2, 2, handle_playlistadd },
{ "playlistclear", PERMISSION_CONTROL, 1, 1, handle_playlistclear },
@@ -164,17 +167,17 @@ static constexpr struct command commands[] = {
{ "playlistsearch", PERMISSION_READ, 1, -1, handle_playlistsearch },
{ "plchanges", PERMISSION_READ, 1, 2, handle_plchanges },
{ "plchangesposid", PERMISSION_READ, 1, 2, handle_plchangesposid },
{ "previous", PERMISSION_CONTROL, 0, 0, handle_previous },
{ "prio", PERMISSION_CONTROL, 2, -1, handle_prio },
{ "prioid", PERMISSION_CONTROL, 2, -1, handle_prioid },
{ "random", PERMISSION_CONTROL, 1, 1, handle_random },
{ "previous", PERMISSION_PLAYER, 0, 0, handle_previous },
{ "prio", PERMISSION_PLAYER, 2, -1, handle_prio },
{ "prioid", PERMISSION_PLAYER, 2, -1, handle_prioid },
{ "random", PERMISSION_PLAYER, 1, 1, handle_random },
{ "rangeid", PERMISSION_ADD, 2, 2, handle_rangeid },
{ "readcomments", PERMISSION_READ, 1, 1, handle_read_comments },
{ "readmessages", PERMISSION_READ, 0, 0, handle_read_messages },
{ "readpicture", PERMISSION_READ, 2, 2, handle_read_picture },
{ "rename", PERMISSION_CONTROL, 2, 2, handle_rename },
{ "repeat", PERMISSION_CONTROL, 1, 1, handle_repeat },
{ "replay_gain_mode", PERMISSION_CONTROL, 1, 1,
{ "repeat", PERMISSION_PLAYER, 1, 1, handle_repeat },
{ "replay_gain_mode", PERMISSION_PLAYER, 1, 1,
handle_replay_gain_mode },
{ "replay_gain_status", PERMISSION_READ, 0, 0,
handle_replay_gain_status },
@@ -186,22 +189,22 @@ static constexpr struct command commands[] = {
{ "searchadd", PERMISSION_ADD, 1, -1, handle_searchadd },
{ "searchaddpl", PERMISSION_CONTROL, 2, -1, handle_searchaddpl },
#endif
{ "seek", PERMISSION_CONTROL, 2, 2, handle_seek },
{ "seekcur", PERMISSION_CONTROL, 1, 1, handle_seekcur },
{ "seekid", PERMISSION_CONTROL, 2, 2, handle_seekid },
{ "seek", PERMISSION_PLAYER, 2, 2, handle_seek },
{ "seekcur", PERMISSION_PLAYER, 1, 1, handle_seekcur },
{ "seekid", PERMISSION_PLAYER, 2, 2, handle_seekid },
{ "sendmessage", PERMISSION_CONTROL, 2, 2, handle_send_message },
{ "setvol", PERMISSION_CONTROL, 1, 1, handle_setvol },
{ "shuffle", PERMISSION_CONTROL, 0, 1, handle_shuffle },
{ "single", PERMISSION_CONTROL, 1, 1, handle_single },
{ "setvol", PERMISSION_PLAYER, 1, 1, handle_setvol },
{ "shuffle", PERMISSION_PLAYER, 0, 1, handle_shuffle },
{ "single", PERMISSION_PLAYER, 1, 1, handle_single },
{ "stats", PERMISSION_READ, 0, 0, handle_stats },
{ "status", PERMISSION_READ, 0, 0, handle_status },
#ifdef ENABLE_SQLITE
{ "sticker", PERMISSION_ADMIN, 3, -1, handle_sticker },
#endif
{ "stop", PERMISSION_CONTROL, 0, 0, handle_stop },
{ "stop", PERMISSION_PLAYER, 0, 0, handle_stop },
{ "subscribe", PERMISSION_READ, 1, 1, handle_subscribe },
{ "swap", PERMISSION_CONTROL, 2, 2, handle_swap },
{ "swapid", PERMISSION_CONTROL, 2, 2, handle_swapid },
{ "swap", PERMISSION_PLAYER, 2, 2, handle_swap },
{ "swapid", PERMISSION_PLAYER, 2, 2, handle_swapid },
{ "tagtypes", PERMISSION_NONE, 0, -1, handle_tagtypes },
{ "toggleoutput", PERMISSION_ADMIN, 1, 1, handle_toggleoutput },
#ifdef ENABLE_DATABASE
@@ -210,7 +213,7 @@ static constexpr struct command commands[] = {
{ "unsubscribe", PERMISSION_READ, 1, 1, handle_unsubscribe },
{ "update", PERMISSION_CONTROL, 0, 1, handle_update },
{ "urlhandlers", PERMISSION_READ, 0, 0, handle_urlhandlers },
{ "volume", PERMISSION_CONTROL, 1, 1, handle_volume },
{ "volume", PERMISSION_PLAYER, 1, 1, handle_volume },
};
static constexpr unsigned num_commands = std::size(commands);
@@ -252,7 +255,7 @@ PrintAvailableCommands(Response &r, const Partition &partition,
if (cmd->permission == (permission & cmd->permission) &&
command_available(partition, cmd))
r.Format("command: %s\n", cmd->cmd);
r.Fmt(FMT_STRING("command: {}\n"), cmd->cmd);
}
return CommandResult::OK;
@@ -265,7 +268,7 @@ PrintUnavailableCommands(Response &r, unsigned permission) noexcept
const struct command *cmd = &i;
if (cmd->permission != (permission & cmd->permission))
r.Format("command: %s\n", cmd->cmd);
r.Fmt(FMT_STRING("command: {}\n"), cmd->cmd);
}
return CommandResult::OK;
@@ -322,9 +325,9 @@ command_check_request(const struct command *cmd, Response &r,
unsigned permission, Request args) noexcept
{
if (cmd->permission != (permission & cmd->permission)) {
r.FormatError(ACK_ERROR_PERMISSION,
"you don't have permission for \"%s\"",
cmd->cmd);
r.FmtError(ACK_ERROR_PERMISSION,
FMT_STRING("you don't have permission for \"{}\""),
cmd->cmd);
return false;
}
@@ -335,17 +338,19 @@ command_check_request(const struct command *cmd, Response &r,
return true;
if (min == max && unsigned(max) != args.size) {
r.FormatError(ACK_ERROR_ARG,
"wrong number of arguments for \"%s\"",
cmd->cmd);
r.FmtError(ACK_ERROR_ARG,
FMT_STRING("wrong number of arguments for \"{}\""),
cmd->cmd);
return false;
} else if (args.size < unsigned(min)) {
r.FormatError(ACK_ERROR_ARG,
"too few arguments for \"%s\"", cmd->cmd);
r.FmtError(ACK_ERROR_ARG,
FMT_STRING("too few arguments for \"{}\""),
cmd->cmd);
return false;
} else if (max >= 0 && args.size > unsigned(max)) {
r.FormatError(ACK_ERROR_ARG,
"too many arguments for \"%s\"", cmd->cmd);
r.FmtError(ACK_ERROR_ARG,
FMT_STRING("too many arguments for \"{}\""),
cmd->cmd);
return false;
} else
return true;
@@ -357,8 +362,8 @@ command_checked_lookup(Response &r, unsigned permission,
{
const struct command *cmd = command_lookup(cmd_name);
if (cmd == nullptr) {
r.FormatError(ACK_ERROR_UNKNOWN,
"unknown command \"%s\"", cmd_name);
r.FmtError(ACK_ERROR_UNKNOWN,
FMT_STRING("unknown command \"{}\""), cmd_name);
return nullptr;
}

Some files were not shown because too many files have changed in this diff Show More