Compare commits
70 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
66a8fac25e | ||
![]() |
1b902e00b4 | ||
![]() |
923e66738c | ||
![]() |
ff3e2c0514 | ||
![]() |
6922a2f55e | ||
![]() |
ca5a400dbe | ||
![]() |
63fe4d1d17 | ||
![]() |
ca06d9d3bf | ||
![]() |
ed2db04f43 | ||
![]() |
de0afa0e08 | ||
![]() |
f0d3227d7b | ||
![]() |
fb07a7cecc | ||
![]() |
c6b08a4d48 | ||
![]() |
040e87ad8d | ||
![]() |
d5521ead56 | ||
![]() |
f8468451c9 | ||
![]() |
65df6ca14e | ||
![]() |
36dec47bf7 | ||
![]() |
478cedcadf | ||
![]() |
cabcbb059d | ||
![]() |
5e21b2db3c | ||
![]() |
3a0d6d96c1 | ||
![]() |
f39d2d33c0 | ||
![]() |
ead3dc6a92 | ||
![]() |
7d814cc899 | ||
![]() |
f5b4606c09 | ||
![]() |
d6dbf64efb | ||
![]() |
8d18b4c24b | ||
![]() |
fe8621906d | ||
![]() |
b4fcbdb235 | ||
![]() |
f4b5a28596 | ||
![]() |
6cbd77fc57 | ||
![]() |
1bc78e9f2c | ||
![]() |
cb6282e0a7 | ||
![]() |
f6941f9a44 | ||
![]() |
d2eb4df8fc | ||
![]() |
df33a898d7 | ||
![]() |
325c7b8e8b | ||
![]() |
380656d8c9 | ||
![]() |
9111bc2c21 | ||
![]() |
37b54179d8 | ||
![]() |
511826763a | ||
![]() |
ef10354d06 | ||
![]() |
158458db5f | ||
![]() |
e183ab5cf8 | ||
![]() |
fef839e2a9 | ||
![]() |
9776e43bbe | ||
![]() |
5201147ab1 | ||
![]() |
fb7daa0d05 | ||
![]() |
2e9f3d8b9f | ||
![]() |
976731ab6c | ||
![]() |
0d8942e64a | ||
![]() |
37a0f04712 | ||
![]() |
cde9348009 | ||
![]() |
095e6e6ad4 | ||
![]() |
9d0bf5e95c | ||
![]() |
8b327f1d9b | ||
![]() |
aef0507abb | ||
![]() |
6bab3bcfea | ||
![]() |
a854595886 | ||
![]() |
8fc3c5c612 | ||
![]() |
4f408bd952 | ||
![]() |
7de8fd04a4 | ||
![]() |
8158bd218c | ||
![]() |
aa1d867b72 | ||
![]() |
34c8242133 | ||
![]() |
e22bdee808 | ||
![]() |
7f87de783f | ||
![]() |
c66389a453 | ||
![]() |
b63c1a2144 |
.travis.ymlNEWS
android
doc
meson.buildpython/build
src
CommandLine.cxxLocateUri.cxxLocateUri.hxxSongLoader.cxx
command
config
db
decoder
plugins
event
BufferedSocket.cxxBufferedSocket.hxxFullyBufferedSocket.hxxServerSocket.cxxServerSocket.hxxSocketMonitor.cxx
filter
haiku
input
lib
gcrypt
neighbor
plugins
net
output
player
zeroconf
systemd
test
win32/res
@@ -9,7 +9,7 @@ matrix:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:mhier/libboost-latest'
|
||||
- sourceline: 'ppa:saiarcot895/chromium-dev' # for ninja-build
|
||||
- sourceline: 'ppa:mstipicevic/ninja-build-1-7-2'
|
||||
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
|
||||
packages:
|
||||
- g++-6
|
||||
@@ -34,7 +34,7 @@ matrix:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:mhier/libboost-latest'
|
||||
- sourceline: 'ppa:saiarcot895/chromium-dev' # for ninja-build
|
||||
- sourceline: 'ppa:mstipicevic/ninja-build-1-7-2'
|
||||
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
|
||||
packages:
|
||||
- g++-8
|
||||
|
31
NEWS
31
NEWS
@@ -1,3 +1,34 @@
|
||||
ver 0.21.9 (2019/05/20)
|
||||
* input
|
||||
- buffer: fix deadlock bug
|
||||
* Android
|
||||
- fix crash on ARMv7
|
||||
- request storage permission on Android 6+
|
||||
* fix spurious "single" mode bug
|
||||
|
||||
ver 0.21.8 (2019/04/23)
|
||||
* input
|
||||
- smbclient: download to buffer instead of throttling transfer
|
||||
* output
|
||||
- httpd: add missing mutex lock
|
||||
- httpd: fix use-after-free bug
|
||||
* playlist
|
||||
- soundcloud: fix "Unsupported URI scheme" (0.21.6 regression)
|
||||
* fix Bonjour bug
|
||||
* fix build failure with GCC 9
|
||||
* fix build failure with -Ddatabase=false
|
||||
* systemd: add user socket unit
|
||||
* doc: "list file" is deprecated
|
||||
|
||||
ver 0.21.7 (2019/04/03)
|
||||
* input
|
||||
- qobuz/tidal: scan tags when loading a playlist
|
||||
* require Meson 0.49.0 for native libgcrypt-config support
|
||||
* fix build failure with -Dlocal_socket=false
|
||||
* Haiku
|
||||
- fix build
|
||||
- add version info
|
||||
|
||||
ver 0.21.6 (2019/03/17)
|
||||
* protocol
|
||||
- allow loading playlists specified as absolute filesystem paths
|
||||
|
@@ -2,8 +2,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.musicpd"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="28"
|
||||
android:versionName="0.21.6">
|
||||
android:versionCode="32"
|
||||
android:versionName="0.21.9">
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
|
||||
|
||||
|
@@ -138,6 +138,12 @@ class AndroidNdkToolchain:
|
||||
libstdcxx_ldflags = libstdcxx_flags + ' -L' + libcxx_libs_path
|
||||
libstdcxx_libs = '-lc++_static -lc++abi'
|
||||
|
||||
if self.is_armv7:
|
||||
# On 32 bit ARM, clang generates no ".eh_frame" section;
|
||||
# instead, the LLVM unwinder library is used for unwinding
|
||||
# the stack after a C++ exception was thrown
|
||||
libstdcxx_libs += ' -lunwind'
|
||||
|
||||
if use_cxx:
|
||||
self.cxxflags += ' ' + libstdcxx_cxxflags
|
||||
self.ldflags += ' ' + libstdcxx_ldflags
|
||||
|
@@ -6,7 +6,7 @@ android_sdk = get_option('android_sdk')
|
||||
android_abi = get_option('android_abi')
|
||||
|
||||
android_sdk_build_tools_version = '27.0.0'
|
||||
android_sdk_platform = 'android-21'
|
||||
android_sdk_platform = 'android-23'
|
||||
|
||||
android_build_tools_dir = join_paths(android_sdk, 'build-tools', android_sdk_build_tools_version)
|
||||
android_sdk_platform_dir = join_paths(android_sdk, 'platforms', android_sdk_platform)
|
||||
|
@@ -21,10 +21,12 @@ package org.musicpd;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
@@ -178,6 +180,14 @@ public class Settings extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
/* TODO: this sure is the wrong place to request
|
||||
permissions - it will cause MPD to quit
|
||||
immediately; we should request permissions when we
|
||||
need them, but implementing that is complicated, so
|
||||
for now, we do it here to give users a quick
|
||||
solution for the problem */
|
||||
requestAllPermissions();
|
||||
|
||||
setContentView(R.layout.settings);
|
||||
mRunButton = (ToggleButton) findViewById(R.id.run);
|
||||
mRunButton.setOnCheckedChangeListener(mOnRunChangeListener);
|
||||
@@ -203,6 +213,31 @@ public class Settings extends Activity {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
private void checkRequestPermission(String permission) {
|
||||
if (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED)
|
||||
return;
|
||||
|
||||
try {
|
||||
this.requestPermissions(new String[]{permission}, 0);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "requestPermissions(" + permission + ") failed",
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
private void requestAllPermissions() {
|
||||
if (android.os.Build.VERSION.SDK_INT < 23)
|
||||
/* we don't need to request permissions on
|
||||
this old Android version */
|
||||
return;
|
||||
|
||||
/* starting with Android 6.0, we need to explicitly
|
||||
request all permissions before using them;
|
||||
mentioning them in the manifest is not enough */
|
||||
|
||||
checkRequestPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
}
|
||||
|
||||
private void connectClient() {
|
||||
mClient = new Main.Client(this, new Main.Client.Callback() {
|
||||
|
||||
|
@@ -38,7 +38,7 @@ author = 'Max Kellermann'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.21.6'
|
||||
version = '0.21.9'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
|
@@ -96,10 +96,8 @@ When the whole patch series is finished, convert stgit patches to git commits:
|
||||
Submitting Patches
|
||||
==================
|
||||
|
||||
Send your patches to the mailing list:
|
||||
Email: `mpd-devel <mpd-devel@musicpd.org>`_
|
||||
|
||||
:program:`git pull` requests are preferred.
|
||||
Submit pull requests on GitHub:
|
||||
https://github.com/MusicPlayerDaemon/MPD/pulls
|
||||
|
||||
Development Tools
|
||||
=================
|
||||
|
@@ -140,7 +140,6 @@ of database.
|
||||
.B auto_update_depth <N>
|
||||
Limit the depth of the directories being watched, 0 means only watch
|
||||
the music directory itself. There is no limit by default.
|
||||
.TP
|
||||
.SH REQUIRED AUDIO OUTPUT PARAMETERS
|
||||
.TP
|
||||
.B type <type>
|
||||
@@ -164,57 +163,12 @@ Specifies how replay gain is applied. The default is "software",
|
||||
which uses an internal software volume control. "mixer" uses the
|
||||
configured (hardware) mixer control. "none" disables replay gain on
|
||||
this audio output.
|
||||
.SH OPTIONAL ALSA OUTPUT PARAMETERS
|
||||
.TP
|
||||
.B device <dev>
|
||||
This specifies the device to use for audio output. The default is "default".
|
||||
.TP
|
||||
.B mixer_type <hardware, software or none>
|
||||
Specifies which mixer should be used for this audio output: the
|
||||
hardware mixer (available for ALSA, OSS and PulseAudio), the software
|
||||
mixer or no mixer ("none"). By default, the hardware mixer is used
|
||||
for devices which support it, and none for the others.
|
||||
.TP
|
||||
.B mixer_device <mixer dev>
|
||||
This specifies which mixer to use. The default is "default". To use
|
||||
the second sound card in a system, use "hw:1".
|
||||
.TP
|
||||
.B mixer_control <mixer ctrl>
|
||||
This specifies which mixer control to use (sometimes referred to as
|
||||
the "device"). The default is "PCM". Use "amixer scontrols" to see
|
||||
the list of possible controls.
|
||||
.TP
|
||||
.B mixer_index <mixer index>
|
||||
A number identifying the index of the named mixer control. This is
|
||||
probably only useful if your alsa device has more than one
|
||||
identically\-named mixer control. The default is "0". Use "amixer
|
||||
scontrols" to see the list of controls with their indexes.
|
||||
.TP
|
||||
.B auto_resample <yes or no>
|
||||
Setting this to "no" disables ALSA's software resampling, if the
|
||||
hardware does not support a specific sample rate. This lets MPD do
|
||||
the resampling. "yes" is the default and allows ALSA to resample.
|
||||
.TP
|
||||
.B auto_channels <yes or no>
|
||||
Setting this to "no" disables ALSA's channel conversion, if the
|
||||
hardware does not support a specific number of channels. Default: "yes".
|
||||
.TP
|
||||
.B auto_format <yes or no>
|
||||
Setting this to "no" disables ALSA's sample format conversion, if the
|
||||
hardware does not support a specific sample format. Default: "yes".
|
||||
.TP
|
||||
.B buffer_time <time in microseconds>
|
||||
This sets the length of the hardware sample buffer in microseconds. Increasing
|
||||
it may help to reduce or eliminate skipping on certain setups. Most users do
|
||||
not need to change this. The default is 500000 microseconds (0.5 seconds).
|
||||
.TP
|
||||
.B period_time <time in microseconds>
|
||||
This sets the time between hardware sample transfers in microseconds.
|
||||
Increasing this can reduce CPU usage while lowering it can reduce underrun
|
||||
errors on bandwidth-limited devices. Some users have reported good results
|
||||
with this set to 50000, but not all devices support values this high. Most
|
||||
users do not need to change this. The default is 256000000 / sample_rate(kHz),
|
||||
or 5804 microseconds for CD-quality audio.
|
||||
.SH FILES
|
||||
.TP
|
||||
.BI ~/.mpdconf
|
||||
|
@@ -690,6 +690,8 @@ Valid quality values for libsoxr:
|
||||
* "low"
|
||||
* "quick"
|
||||
|
||||
.. _output_plugins:
|
||||
|
||||
Output plugins
|
||||
--------------
|
||||
|
||||
@@ -800,6 +802,15 @@ The fifo plugin writes raw PCM data to a FIFO (First In, First Out) file. The da
|
||||
* - **path P**
|
||||
- This specifies the path of the FIFO to write to. Must be an absolute path. If the path does not exist, it will be created when MPD is started, and removed when MPD is stopped. The FIFO will be created with the same user and group as MPD is running as. Default permissions can be modified by using the builtin shell command umask. If a FIFO already exists at the specified path it will be reused, and will not be removed when MPD is stopped. You can use the "mkfifo" command to create this, and then you may modify the permissions to your liking.
|
||||
|
||||
haiku
|
||||
~~~~~
|
||||
|
||||
Use the SoundPlayer API on the Haiku operating system.
|
||||
|
||||
This plugin is unmaintained and contains known bugs. It will be
|
||||
removed soon, unless there is a new maintainer.
|
||||
|
||||
|
||||
jack
|
||||
~~~~
|
||||
The jack plugin connects to a `JACK server <http://jackaudio.org/>`_.
|
||||
@@ -838,7 +849,7 @@ It is highly recommended to configure a fixed format, because a stream cannot sw
|
||||
* - **port P**
|
||||
- Binds the HTTP server to the specified port.
|
||||
* - **bind_to_address ADDR**
|
||||
- Binds the HTTP server to the specified address (IPv4, IPv6 or UNIX socket). Multiple addresses in parallel are not supported.
|
||||
- Binds the HTTP server to the specified address (IPv4, IPv6 or local socket). Multiple addresses in parallel are not supported.
|
||||
* - **encoder NAME**
|
||||
- Chooses an encoder plugin. A list of encoder plugins can be found in the encoder plugin reference :ref:`encoder_plugins`.
|
||||
* - **max_clients MC**
|
||||
@@ -1037,8 +1048,41 @@ The "Solaris" plugin runs only on SUN Solaris, and plays via /dev/audio.
|
||||
* - **device PATH**
|
||||
- Sets the path of the audio device, defaults to /dev/audio.
|
||||
|
||||
|
||||
.. _filter_plugins:
|
||||
|
||||
Filter plugins
|
||||
--------------
|
||||
|
||||
normalize
|
||||
~~~~~~~~~
|
||||
|
||||
Normalize the volume during playback (at the expensve of quality).
|
||||
|
||||
|
||||
null
|
||||
~~~~
|
||||
|
||||
A no-op filter. Audio data is returned as-is.
|
||||
|
||||
|
||||
route
|
||||
~~~~~
|
||||
|
||||
Reroute channels.
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
:header-rows: 1
|
||||
|
||||
* - Setting
|
||||
- Description
|
||||
* - **routes "0>0, 1>1, ..."**
|
||||
- Specifies the channel mapping.
|
||||
|
||||
|
||||
.. _playlist_plugins:
|
||||
|
||||
|
||||
Playlist plugins
|
||||
----------------
|
||||
|
||||
|
@@ -14,6 +14,9 @@ Once the client is connected to the server, they conduct a
|
||||
conversation until the client closes the connection. The
|
||||
conversation flow is always initiated by the client.
|
||||
|
||||
All data between the client and the server is encoded in
|
||||
UTF-8.
|
||||
|
||||
The client transmits a command sequence, terminated by the
|
||||
newline character ``\n``. The server will
|
||||
respond with one or more lines, the last of which will be a
|
||||
@@ -42,9 +45,6 @@ quotation marks.
|
||||
Argument strings are separated from the command and any other
|
||||
arguments by linear white-space (' ' or '\\t').
|
||||
|
||||
All data between the client and the server is encoded in
|
||||
UTF-8.
|
||||
|
||||
Responses
|
||||
=========
|
||||
|
||||
@@ -52,6 +52,28 @@ A command returns ``OK`` on completion or
|
||||
``ACK some error`` on failure. These
|
||||
denote the end of command execution.
|
||||
|
||||
Some commands return more data before the response ends with ``OK``.
|
||||
Each line is usually in the form ``NAME: VALUE``. Example::
|
||||
|
||||
foo: bar
|
||||
OK
|
||||
|
||||
.. _binary:
|
||||
|
||||
Binary Responses
|
||||
----------------
|
||||
|
||||
Some commands can return binary data. This is initiated by a line
|
||||
containing ``binary: 1234`` (followed as usual by a newline). After
|
||||
that, the specified number of bytes of binary data follows (without an
|
||||
extra newline, because this binary data is not a text line), and
|
||||
finally the ``OK`` line. Example::
|
||||
|
||||
foo: bar
|
||||
binary: 42
|
||||
<42 bytes>OK
|
||||
|
||||
|
||||
Failure responses
|
||||
-----------------
|
||||
|
||||
@@ -112,9 +134,9 @@ list begins with `command_list_begin` or
|
||||
`command_list_ok_begin` and ends with
|
||||
`command_list_end`.
|
||||
|
||||
It does not execute any commands until the list has ended.
|
||||
The return value is whatever the return for a list of commands
|
||||
is. On success for all commands,
|
||||
It does not execute any commands until the list has ended. The
|
||||
response is a concatentation of all individual responses.
|
||||
On success for all commands,
|
||||
``OK`` is returned. If a command
|
||||
fails, no more commands are executed and the appropriate
|
||||
``ACK`` error is returned. If
|
||||
@@ -178,8 +200,9 @@ of:
|
||||
file's time stamp with the given value (ISO 8601 or UNIX
|
||||
time stamp).
|
||||
|
||||
- ``(AudioFormat == 'SAMPLERATE:BITS:CHANNELS')``:
|
||||
compares the audio format with the given value.
|
||||
- ``(AudioFormat == 'SAMPLERATE:BITS:CHANNELS')``: compares the audio
|
||||
format with the given value. See :ref:`audio_output_format` for a
|
||||
detailed explanation.
|
||||
|
||||
- ``(AudioFormat =~ 'SAMPLERATE:BITS:CHANNELS')``:
|
||||
matches the audio format with the given mask (i.e. one
|
||||
@@ -414,15 +437,18 @@ Querying :program:`MPD`'s status
|
||||
- ``songid``: playlist songid of the current song stopped on or playing
|
||||
- ``nextsong`` [#since_0_15]_: playlist song number of the next song to be played
|
||||
- ``nextsongid`` [#since_0_15]_: playlist songid of the next song to be played
|
||||
- ``time``: total time elapsed (of current playing/paused song)
|
||||
- ``time``: total time elapsed (of current playing/paused song) in seconds
|
||||
(deprecated, use ``elapsed`` instead)
|
||||
- ``elapsed`` [#since_0_16]_: Total time elapsed within the current song, but with higher resolution.
|
||||
- ``elapsed`` [#since_0_16]_: Total time elapsed within the
|
||||
current song in seconds, but with higher resolution.
|
||||
- ``duration`` [#since_0_20]_: Duration of the current song in seconds.
|
||||
- ``bitrate``: instantaneous bitrate in kbps
|
||||
- ``xfade``: ``crossfade`` in seconds
|
||||
- ``mixrampdb``: ``mixramp`` threshold in dB
|
||||
- ``mixrampdelay``: ``mixrampdelay`` in seconds
|
||||
- ``audio``: The format emitted by the decoder plugin during playback, format: ``*samplerate:bits:channels*``. Check the user manual for a detailed explanation.
|
||||
- ``audio``: The format emitted by the decoder plugin during
|
||||
playback, format: ``samplerate:bits:channels``. See
|
||||
:ref:`audio_output_format` for a detailed explanation.
|
||||
- ``updating_db``: ``job id``
|
||||
- ``error``: if there is an error, returns message here
|
||||
|
||||
@@ -437,7 +463,7 @@ Querying :program:`MPD`'s status
|
||||
- ``albums``: number of albums
|
||||
- ``songs``: number of songs
|
||||
- ``uptime``: daemon uptime in seconds
|
||||
- ``db_playtime``: sum of all song times in the db
|
||||
- ``db_playtime``: sum of all song times in the database in seconds
|
||||
- ``db_update``: last db update in UNIX time
|
||||
- ``playtime``: time length of music played
|
||||
|
||||
@@ -599,7 +625,7 @@ Whenever possible, ids should be used.
|
||||
Deletes the song ``SONGID`` from the
|
||||
playlist
|
||||
|
||||
:command:`move {FROM} [{START:END} | {TO}]`
|
||||
:command:`move [{FROM} | {START:END}] {TO}`
|
||||
Moves the song at ``FROM`` or range of songs
|
||||
at ``START:END`` [#since_0_15]_ to ``TO``
|
||||
in the playlist.
|
||||
@@ -721,7 +747,7 @@ run playlist plugins instead of the hard-coded simple
|
||||
`m3u` parser. They can access playlists in
|
||||
the music directory (relative path including the suffix),
|
||||
playlists in arbitrary location (absolute path including the suffix;
|
||||
allowed only for clients that are connected via UNIX domain socket), or
|
||||
allowed only for clients that are connected via local socket), or
|
||||
remote playlists (absolute URI with a supported scheme).
|
||||
|
||||
:command:`listplaylist {NAME}`
|
||||
@@ -790,7 +816,7 @@ The music database
|
||||
|
||||
Returns the file size and actual number
|
||||
of bytes read at the requested offset, followed
|
||||
by the chunk requested as raw bytes, then a
|
||||
by the chunk requested as raw bytes (see :ref:`binary`), then a
|
||||
newline and the completion code.
|
||||
|
||||
Example::
|
||||
@@ -798,8 +824,7 @@ The music database
|
||||
albumart
|
||||
size: 1024768
|
||||
binary: 8192
|
||||
<8192 bytes>
|
||||
OK
|
||||
<8192 bytes>OK
|
||||
|
||||
:command:`count {FILTER} [group {GROUPTYPE}]`
|
||||
Count the number of songs and their total playtime in
|
||||
@@ -860,8 +885,7 @@ The music database
|
||||
:command:`list {TYPE} {FILTER} [group {GROUPTYPE}]`
|
||||
Lists unique tags values of the specified type.
|
||||
``TYPE`` can be any tag supported by
|
||||
:program:`MPD` or
|
||||
*file*.
|
||||
:program:`MPD`.
|
||||
|
||||
Additional arguments may specify a :ref:`filter <filter_syntax>`.
|
||||
The *group* keyword may be used
|
||||
@@ -872,6 +896,10 @@ The music database
|
||||
|
||||
list album group albumartist
|
||||
|
||||
``list file`` was implemented in an early :program:`MPD` version,
|
||||
but does not appear to make a lot of sense. It still works (to
|
||||
avoid breaking compatibility), but is deprecated.
|
||||
|
||||
.. _command_listall:
|
||||
|
||||
:command:`listall [URI]`
|
||||
@@ -931,7 +959,7 @@ The music database
|
||||
This command may be used to list metadata of remote
|
||||
files (e.g. URI beginning with "http://" or "smb://").
|
||||
|
||||
Clients that are connected via UNIX domain socket may
|
||||
Clients that are connected via local socket may
|
||||
use this command to read the tags of an arbitrary local
|
||||
file (URI is an absolute path).
|
||||
|
||||
@@ -1053,7 +1081,8 @@ Stickers
|
||||
"Stickers" [#since_0_15]_ are pieces of
|
||||
information attached to existing
|
||||
:program:`MPD` objects (e.g. song files,
|
||||
directories, albums). Clients can create arbitrary name/value
|
||||
directories, albums; but currently, they are only implemented for
|
||||
song). Clients can create arbitrary name/value
|
||||
pairs. :program:`MPD` itself does not assume
|
||||
any special meaning in them.
|
||||
|
||||
@@ -1222,7 +1251,7 @@ Reflection
|
||||
:command:`config`
|
||||
Dumps configuration values that may be interesting for
|
||||
the client. This command is only permitted to "local"
|
||||
clients (connected via UNIX domain socket).
|
||||
clients (connected via local socket).
|
||||
|
||||
The following response attributes are available:
|
||||
|
||||
|
61
doc/user.rst
61
doc/user.rst
@@ -54,7 +54,7 @@ Download the source tarball from the `MPD home page <https://musicpd.org>`_ and
|
||||
In any case, you need:
|
||||
|
||||
* a C++14 compiler (e.g. gcc 6.0 or clang 3.9)
|
||||
* `Meson 0.47.2 <http://mesonbuild.com/>`__ and `Ninja
|
||||
* `Meson 0.49.0 <http://mesonbuild.com/>`__ and `Ninja
|
||||
<https://ninja-build.org/>`__
|
||||
* Boost 1.58
|
||||
* pkg-config
|
||||
@@ -365,10 +365,14 @@ More information can be found in the :ref:`decoder_plugins` reference.
|
||||
Configuring encoder plugins
|
||||
---------------------------
|
||||
|
||||
Encoders are used by some of the output plugins (such as shout). The encoder settings are included in the audio_output section.
|
||||
Encoders are used by some of the output plugins (such as shout). The
|
||||
encoder settings are included in the ``audio_output`` section, see :ref:`config_audio_output`.
|
||||
|
||||
More information can be found in the :ref:`encoder_plugins` reference.
|
||||
|
||||
|
||||
.. _config_audio_output:
|
||||
|
||||
Configuring audio outputs
|
||||
-------------------------
|
||||
|
||||
@@ -398,14 +402,9 @@ The following table lists the audio_output options valid for all plugins:
|
||||
- The name of the plugin
|
||||
* - **name**
|
||||
- The name of the audio output. It is visible to the client. Some plugins also use it internally, e.g. as a name registered in the PULSE server.
|
||||
* - **format**
|
||||
- Always open the audio output with the specified audio format samplerate:bits:channels), regardless of the format of the input file. This is optional for most plugins.
|
||||
|
||||
Any of the three attributes may be an asterisk to specify that this attribute should not be enforced, example: 48000:16:*. *:*:* is equal to not having a format specification.
|
||||
|
||||
The following values are valid for bits: 8 (signed 8 bit integer samples), 16, 24 (signed 24 bit integer samples padded to 32 bit), 32 (signed 32 bit integer samples), f (32 bit floating point, -1.0 to 1.0), "dsd" means DSD (Direct Stream Digital). For DSD, there are special cases such as "dsd64", which allows you to omit the sample rate (e.g. dsd512:2 for stereo DSD512, i.e. 22.5792 MHz).
|
||||
|
||||
The sample rate is special for DSD: :program:`MPD` counts the number of bytes, not bits. Thus, a DSD "bit" rate of 22.5792 MHz (DSD512) is 2822400 from :program:`MPD`'s point of view (44100*512/8).
|
||||
* - **format samplerate:bits:channels**
|
||||
- Always open the audio output with the specified audio format, regardless of the format of the input file. This is optional for most plugins.
|
||||
See :ref:`audio_output_format` for a detailed description of the value.
|
||||
* - **enabed yes|no**
|
||||
- Specifies whether this audio output is enabled when :program:`MPD` is started. By default, all audio outputs are enabled. This is just the default setting when there is no state file; with a state file, the previous state is restored.
|
||||
* - **tags yes|no**
|
||||
@@ -421,6 +420,15 @@ The following table lists the audio_output options valid for all plugins:
|
||||
implement an external mixer :ref:`external_mixer`) or no mixer
|
||||
(:samp:`none`). By default, the hardware mixer is used for
|
||||
devices which support it, and none for the others.
|
||||
* - **filters "name,...**"
|
||||
- The specified configured filters are instantiated in the given
|
||||
order. Each filter name refers to a ``filter`` block, see
|
||||
:ref:`config_filter`.
|
||||
|
||||
More information can be found in the :ref:`output_plugins` reference.
|
||||
|
||||
|
||||
.. _config_filter:
|
||||
|
||||
Configuring filters
|
||||
-------------------
|
||||
@@ -436,6 +444,9 @@ To configure a filter, add a :code:`filter` block to :file:`mpd.conf`:
|
||||
name "software volume"
|
||||
}
|
||||
|
||||
Configured filters may then be added to the ``filters`` setting of an
|
||||
``audio_output`` section, see :ref:`config_audio_output`.
|
||||
|
||||
The following table lists the filter options valid for all plugins:
|
||||
|
||||
.. list-table::
|
||||
@@ -449,6 +460,9 @@ The following table lists the filter options valid for all plugins:
|
||||
* - **name**
|
||||
- The name of the filter
|
||||
|
||||
More information can be found in the :ref:`filter_plugins` reference.
|
||||
|
||||
|
||||
Configuring playlist plugins
|
||||
----------------------------
|
||||
|
||||
@@ -485,10 +499,31 @@ reference.
|
||||
Audio Format Settings
|
||||
---------------------
|
||||
|
||||
.. _audio_output_format:
|
||||
|
||||
Global Audio Format
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The setting audio_output_format forces :program:`MPD` to use one audio format for all outputs. Doing that is usually not a good idea. The values are the same as in format in the audio_output section.
|
||||
The setting ``audio_output_format`` forces :program:`MPD` to use one
|
||||
audio format for all outputs. Doing that is usually not a good idea.
|
||||
|
||||
The value is specified as ``samplerate:bits:channels``.
|
||||
|
||||
Any of the three attributes may be an asterisk to specify that this
|
||||
attribute should not be enforced, example: ``48000:16:*``.
|
||||
``*:*:*`` is equal to not having a format specification.
|
||||
|
||||
The following values are valid for bits: ``8`` (signed 8 bit integer
|
||||
samples), ``16``, ``24`` (signed 24 bit integer samples padded to 32
|
||||
bit), ``32`` (signed 32 bit integer samples), ``f`` (32 bit floating
|
||||
point, -1.0 to 1.0), ``dsd`` means DSD (Direct Stream Digital). For
|
||||
DSD, there are special cases such as ``dsd64``, which allows you to
|
||||
omit the sample rate (e.g. ``dsd512:2`` for stereo DSD512,
|
||||
i.e. 22.5792 MHz).
|
||||
|
||||
The sample rate is special for DSD: :program:`MPD` counts the number
|
||||
of bytes, not bits. Thus, a DSD "bit" rate of 22.5792 MHz (DSD512) is
|
||||
2822400 from :program:`MPD`'s point of view (44100*512/8).
|
||||
|
||||
Resampler
|
||||
~~~~~~~~~
|
||||
@@ -866,7 +901,7 @@ To verify if :program:`MPD` converts the audio format, enable verbose logging, a
|
||||
.. code-block:: none
|
||||
|
||||
decoder: audio_format=44100:24:2, seekable=true
|
||||
output: opened plugin=alsa name="An ALSA output"audio_format=44100:16:2
|
||||
output: opened plugin=alsa name="An ALSA output" audio_format=44100:16:2
|
||||
output: converting from 44100:24:2
|
||||
|
||||
This example shows that a 24 bit file is being played, but the sound chip cannot play 24 bit. It falls back to 16 bit, discarding 8 bit.
|
||||
@@ -893,7 +928,7 @@ Check list for bit-perfect playback:
|
||||
device (:samp:`hw:0,0` or similar).
|
||||
* Don't use software volume (setting :code:`mixer_type`).
|
||||
* Don't force :program:`MPD` to use a specific audio format (settings
|
||||
:code:`format`, :code:`audio_output_format`).
|
||||
:code:`format`, :ref:`audio_output_format <audio_output_format>`).
|
||||
* Verify that you are really doing bit-perfect playback using :program:`MPD`'s verbose log and :file:`/proc/asound/card*/pcm*p/sub*/hw_params`. Some DACs can also indicate the audio format.
|
||||
|
||||
Direct Stream Digital (DSD)
|
||||
|
27
meson.build
27
meson.build
@@ -1,8 +1,8 @@
|
||||
project(
|
||||
'mpd',
|
||||
['c', 'cpp'],
|
||||
version: '0.21.6',
|
||||
meson_version: '>= 0.47.2',
|
||||
version: '0.21.9',
|
||||
meson_version: '>= 0.49.0',
|
||||
default_options: [
|
||||
'c_std=c99',
|
||||
'cpp_std=c++14'
|
||||
@@ -367,8 +367,10 @@ basic_dep = declare_dependency(
|
||||
|
||||
if enable_database
|
||||
subdir('src/storage')
|
||||
subdir('src/db')
|
||||
else
|
||||
storage_glue_dep = dependency('', required: false)
|
||||
endif
|
||||
subdir('src/db')
|
||||
|
||||
if neighbor_glue_dep.found()
|
||||
sources += 'src/command/NeighborCommands.cxx'
|
||||
@@ -390,6 +392,7 @@ more_deps = []
|
||||
if is_android
|
||||
subdir('src/java')
|
||||
target_type = 'shared_library'
|
||||
target_name = 'mpd'
|
||||
link_args += [
|
||||
'-Wl,--no-undefined,-shared,-Bsymbolic',
|
||||
'-llog',
|
||||
@@ -399,12 +402,20 @@ if is_android
|
||||
declare_dependency(sources: [classes_jar]),
|
||||
java_dep,
|
||||
]
|
||||
elif is_haiku
|
||||
target_type = 'executable'
|
||||
target_name = 'mpd.nores'
|
||||
link_args += [
|
||||
'-lnetwork',
|
||||
'-lbe',
|
||||
]
|
||||
else
|
||||
target_type = 'executable'
|
||||
target_name = 'mpd'
|
||||
endif
|
||||
|
||||
mpd = build_target(
|
||||
'mpd',
|
||||
target_name,
|
||||
sources,
|
||||
target_type: target_type,
|
||||
include_directories: inc,
|
||||
@@ -443,6 +454,14 @@ endif
|
||||
|
||||
if is_haiku
|
||||
subdir('src/haiku')
|
||||
custom_target(
|
||||
'mpd',
|
||||
output: 'mpd',
|
||||
input: [mpd, rsrc],
|
||||
command: [addres, '@OUTPUT@', '@INPUT0@', '@INPUT1@'],
|
||||
install: true,
|
||||
install_dir: get_option('bindir'),
|
||||
)
|
||||
endif
|
||||
|
||||
configure_file(output: 'config.h', configuration: conf)
|
||||
|
@@ -112,8 +112,8 @@ liblame = AutotoolsProject(
|
||||
)
|
||||
|
||||
ffmpeg = FfmpegProject(
|
||||
'http://ffmpeg.org/releases/ffmpeg-4.1.1.tar.xz',
|
||||
'373749824dfd334d84e55dff406729edfd1606575ee44dd485d97d45ea4d2d86',
|
||||
'http://ffmpeg.org/releases/ffmpeg-4.1.3.tar.xz',
|
||||
'0c3020452880581a8face91595b239198078645e7d7184273b8bcc7758beb63d',
|
||||
'lib/libavcodec.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -341,8 +341,8 @@ ffmpeg = FfmpegProject(
|
||||
)
|
||||
|
||||
curl = AutotoolsProject(
|
||||
'http://curl.haxx.se/download/curl-7.64.0.tar.xz',
|
||||
'2f2f13fa34d44aa29cb444077ad7dc4dc6d189584ad552e0aaeb06e608af6001',
|
||||
'http://curl.haxx.se/download/curl-7.64.1.tar.xz',
|
||||
'9252332a7f871ce37bfa7f78bdd0a0e3924d8187cc27cb57c76c9474a7168fb3',
|
||||
'lib/libcurl.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -375,8 +375,8 @@ libexpat = AutotoolsProject(
|
||||
)
|
||||
|
||||
libnfs = AutotoolsProject(
|
||||
'https://github.com/sahlberg/libnfs/archive/libnfs-3.0.0.tar.gz',
|
||||
'445d92c5fc55e4a5b115e358e60486cf8f87ee50e0103d46a02e7fb4618566a5',
|
||||
'https://github.com/sahlberg/libnfs/archive/libnfs-4.0.0.tar.gz',
|
||||
'6ee77e9fe220e2d3e3b1f53cfea04fb319828cc7dbb97dd9df09e46e901d797d',
|
||||
'lib/libnfs.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -387,12 +387,12 @@ libnfs = AutotoolsProject(
|
||||
|
||||
'--disable-utils', '--disable-examples',
|
||||
],
|
||||
base='libnfs-libnfs-3.0.0',
|
||||
base='libnfs-libnfs-4.0.0',
|
||||
autoreconf=True,
|
||||
)
|
||||
|
||||
boost = BoostProject(
|
||||
'http://downloads.sourceforge.net/project/boost/boost/1.69.0/boost_1_69_0.tar.bz2',
|
||||
'8f32d4617390d1c2d16f26a27ab60d97807b35440d45891fa340fc2648b04406',
|
||||
'http://downloads.sourceforge.net/project/boost/boost/1.70.0/boost_1_70_0.tar.bz2',
|
||||
'430ae8354789de4fd19ee52f3b1f739e1fba576f0aded0897c3c2bc00fb38778',
|
||||
'include/boost/version.hpp',
|
||||
)
|
||||
|
@@ -108,17 +108,17 @@ static constexpr Domain cmdline_domain("cmdline");
|
||||
gcc_noreturn
|
||||
static void version(void)
|
||||
{
|
||||
printf("Music Player Daemon " VERSION " (%s)\n"
|
||||
printf("Music Player Daemon " VERSION " (%s)"
|
||||
"\n"
|
||||
"Copyright 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n"
|
||||
"Copyright 2008-2018 Max Kellermann <max.kellermann@gmail.com>\n"
|
||||
"This is free software; see the source for copying conditions. There is NO\n"
|
||||
"warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
|
||||
"warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
|
||||
GIT_VERSION);
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
"\n"
|
||||
"Database plugins:\n",
|
||||
GIT_VERSION);
|
||||
printf("\n"
|
||||
"Database plugins:\n");
|
||||
|
||||
for (auto i = database_plugins; *i != nullptr; ++i)
|
||||
printf(" %s", (*i)->name);
|
||||
@@ -129,18 +129,18 @@ static void version(void)
|
||||
for (auto i = storage_plugins; *i != nullptr; ++i)
|
||||
printf(" %s", (*i)->name);
|
||||
|
||||
printf("\n"
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
"\n"
|
||||
printf("\n"
|
||||
"Neighbor plugins:\n");
|
||||
for (auto i = neighbor_plugins; *i != nullptr; ++i)
|
||||
printf(" %s", (*i)->name);
|
||||
|
||||
printf("\n"
|
||||
#endif
|
||||
|
||||
printf("\n"
|
||||
"\n"
|
||||
"Decoders plugins:\n");
|
||||
|
||||
|
@@ -55,14 +55,26 @@ LocateFileUri(const char *uri, const Client *client
|
||||
}
|
||||
|
||||
static LocatedUri
|
||||
LocateAbsoluteUri(const char *uri
|
||||
LocateAbsoluteUri(UriPluginKind kind, const char *uri
|
||||
#ifdef ENABLE_DATABASE
|
||||
, const Storage *storage
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (!uri_supported_scheme(uri))
|
||||
throw std::runtime_error("Unsupported URI scheme");
|
||||
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");
|
||||
break;
|
||||
|
||||
case UriPluginKind::PLAYLIST:
|
||||
/* for now, no validation for playlist URIs; this is
|
||||
more complicated because there are three ways to
|
||||
identify which plugin to use: URI scheme, filename
|
||||
suffix and MIME type */
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
if (storage != nullptr) {
|
||||
@@ -76,7 +88,8 @@ LocateAbsoluteUri(const char *uri
|
||||
}
|
||||
|
||||
LocatedUri
|
||||
LocateUri(const char *uri, const Client *client
|
||||
LocateUri(UriPluginKind kind,
|
||||
const char *uri, const Client *client
|
||||
#ifdef ENABLE_DATABASE
|
||||
, const Storage *storage
|
||||
#endif
|
||||
@@ -100,7 +113,7 @@ LocateUri(const char *uri, const Client *client
|
||||
#endif
|
||||
);
|
||||
else if (uri_has_scheme(uri))
|
||||
return LocateAbsoluteUri(uri
|
||||
return LocateAbsoluteUri(kind, uri
|
||||
#ifdef ENABLE_DATABASE
|
||||
, storage
|
||||
#endif
|
||||
|
@@ -41,6 +41,12 @@ class Client;
|
||||
class Storage;
|
||||
#endif
|
||||
|
||||
enum class UriPluginKind {
|
||||
INPUT,
|
||||
STORAGE,
|
||||
PLAYLIST,
|
||||
};
|
||||
|
||||
struct LocatedUri {
|
||||
enum class Type {
|
||||
/**
|
||||
@@ -84,7 +90,8 @@ struct LocatedUri {
|
||||
* that feature is disabled if this parameter is nullptr
|
||||
*/
|
||||
LocatedUri
|
||||
LocateUri(const char *uri, const Client *client
|
||||
LocateUri(UriPluginKind kind,
|
||||
const char *uri, const Client *client
|
||||
#ifdef ENABLE_DATABASE
|
||||
, const Storage *storage
|
||||
#endif
|
||||
|
@@ -94,7 +94,8 @@ SongLoader::LoadSong(const char *uri_utf8) const
|
||||
assert(uri_utf8 != nullptr);
|
||||
#endif
|
||||
|
||||
const auto located_uri = LocateUri(uri_utf8, client
|
||||
const auto located_uri = LocateUri(UriPluginKind::INPUT,
|
||||
uri_utf8, client
|
||||
#ifdef ENABLE_DATABASE
|
||||
, storage
|
||||
#endif
|
||||
|
@@ -218,7 +218,7 @@ handle_read_comments(Client &client, Request args, Response &r)
|
||||
|
||||
const char *const uri = args.front();
|
||||
|
||||
const auto located_uri = LocateUri(uri, &client
|
||||
const auto located_uri = LocateUri(UriPluginKind::INPUT, uri, &client
|
||||
#ifdef ENABLE_DATABASE
|
||||
, nullptr
|
||||
#endif
|
||||
@@ -331,7 +331,7 @@ handle_album_art(Client &client, Request args, Response &r)
|
||||
const char *uri = args.front();
|
||||
size_t offset = args.ParseUnsigned(1);
|
||||
|
||||
const auto located_uri = LocateUri(uri, &client
|
||||
const auto located_uri = LocateUri(UriPluginKind::INPUT, uri, &client
|
||||
#ifdef ENABLE_DATABASE
|
||||
, nullptr
|
||||
#endif
|
||||
|
@@ -99,7 +99,7 @@ handle_listfiles(Client &client, Request args, Response &r)
|
||||
/* default is root directory */
|
||||
const auto uri = args.GetOptional(0, "");
|
||||
|
||||
const auto located_uri = LocateUri(uri, &client
|
||||
const auto located_uri = LocateUri(UriPluginKind::STORAGE, uri, &client
|
||||
#ifdef ENABLE_DATABASE
|
||||
, nullptr
|
||||
#endif
|
||||
@@ -219,7 +219,7 @@ handle_lsinfo(Client &client, Request args, Response &r)
|
||||
compatibility, work around this here */
|
||||
uri = "";
|
||||
|
||||
const auto located_uri = LocateUri(uri, &client
|
||||
const auto located_uri = LocateUri(UriPluginKind::INPUT, uri, &client
|
||||
#ifdef ENABLE_DATABASE
|
||||
, nullptr
|
||||
#endif
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#include "config.h"
|
||||
#include "PlaylistCommands.hxx"
|
||||
#include "Request.hxx"
|
||||
#include "Instance.hxx"
|
||||
#include "db/DatabasePlaylist.hxx"
|
||||
#include "CommandError.hxx"
|
||||
#include "PlaylistSave.hxx"
|
||||
@@ -27,6 +28,7 @@
|
||||
#include "PlaylistError.hxx"
|
||||
#include "db/PlaylistVector.hxx"
|
||||
#include "SongLoader.hxx"
|
||||
#include "song/DetachedSong.hxx"
|
||||
#include "BulkEdit.hxx"
|
||||
#include "playlist/PlaylistQueue.hxx"
|
||||
#include "playlist/Print.hxx"
|
||||
@@ -67,7 +69,8 @@ handle_save(Client &client, Request args, gcc_unused Response &r)
|
||||
CommandResult
|
||||
handle_load(Client &client, Request args, gcc_unused Response &r)
|
||||
{
|
||||
const auto uri = LocateUri(args.front(), &client
|
||||
const auto uri = LocateUri(UriPluginKind::PLAYLIST, args.front(),
|
||||
&client
|
||||
#ifdef ENABLE_DATABASE
|
||||
, nullptr
|
||||
#endif
|
||||
@@ -76,18 +79,29 @@ handle_load(Client &client, Request args, gcc_unused Response &r)
|
||||
|
||||
const ScopeBulkEdit bulk_edit(client.GetPartition());
|
||||
|
||||
auto &playlist = client.GetPlaylist();
|
||||
const unsigned old_size = playlist.GetLength();
|
||||
|
||||
const SongLoader loader(client);
|
||||
playlist_open_into_queue(uri,
|
||||
range.start, range.end,
|
||||
client.GetPlaylist(),
|
||||
playlist,
|
||||
client.GetPlayerControl(), loader);
|
||||
|
||||
/* invoke the RemoteTagScanner on all newly added songs */
|
||||
auto &instance = client.GetInstance();
|
||||
const unsigned new_size = playlist.GetLength();
|
||||
for (unsigned i = old_size; i < new_size; ++i)
|
||||
instance.LookupRemoteTag(playlist.queue.Get(i).GetURI());
|
||||
|
||||
return CommandResult::OK;
|
||||
}
|
||||
|
||||
CommandResult
|
||||
handle_listplaylist(Client &client, Request args, Response &r)
|
||||
{
|
||||
const auto name = LocateUri(args.front(), &client
|
||||
const auto name = LocateUri(UriPluginKind::PLAYLIST, args.front(),
|
||||
&client
|
||||
#ifdef ENABLE_DATABASE
|
||||
, nullptr
|
||||
#endif
|
||||
@@ -103,7 +117,8 @@ handle_listplaylist(Client &client, Request args, Response &r)
|
||||
CommandResult
|
||||
handle_listplaylistinfo(Client &client, Request args, Response &r)
|
||||
{
|
||||
const auto name = LocateUri(args.front(), &client
|
||||
const auto name = LocateUri(UriPluginKind::PLAYLIST, args.front(),
|
||||
&client
|
||||
#ifdef ENABLE_DATABASE
|
||||
, nullptr
|
||||
#endif
|
||||
|
@@ -83,7 +83,8 @@ handle_add(Client &client, Request args, Response &r)
|
||||
here */
|
||||
uri = "";
|
||||
|
||||
const auto located_uri = LocateUri(uri, &client
|
||||
const auto located_uri = LocateUri(UriPluginKind::INPUT, uri,
|
||||
&client
|
||||
#ifdef ENABLE_DATABASE
|
||||
, nullptr
|
||||
#endif
|
||||
|
@@ -29,10 +29,8 @@ ServerSocketAddGeneric(ServerSocket &server_socket, const char *address, unsigne
|
||||
server_socket.AddPort(port);
|
||||
} else if (address[0] == '/' || address[0] == '~') {
|
||||
server_socket.AddPath(ParsePath(address));
|
||||
#ifdef __linux__
|
||||
} else if (address[0] == '@') {
|
||||
server_socket.AddAbstract(address);
|
||||
#endif
|
||||
} else {
|
||||
server_socket.AddHost(address, port);
|
||||
}
|
||||
|
@@ -23,14 +23,14 @@
|
||||
class ServerSocket;
|
||||
|
||||
/**
|
||||
* Sets the address or unix socket of a ServerSocket instance
|
||||
* Sets the address or local socket of a ServerSocket instance
|
||||
* There are three possible ways
|
||||
* 1) Set address to a valid ip address and specify port.
|
||||
* server_socket will listen on this address/port tuple.
|
||||
* 2) Set address to null and specify port.
|
||||
* server_socket will listen on ANY address on that port.
|
||||
* 3) Set address to a path of a unix socket. port is ignored.
|
||||
* server_socket will listen on this unix socket.
|
||||
* 3) Set address to a path of a local socket. port is ignored.
|
||||
* server_socket will listen on this local socket.
|
||||
*
|
||||
* Throws #std::runtime_error on error.
|
||||
*
|
||||
|
@@ -9,6 +9,11 @@ db_api_dep = declare_dependency(
|
||||
link_with: db_api,
|
||||
)
|
||||
|
||||
if not enable_database
|
||||
db_glue_dep = db_api_dep
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
subdir('plugins')
|
||||
|
||||
db_glue_sources = [
|
||||
|
@@ -568,7 +568,8 @@ ProxyDatabase::OnSocketReady(gcc_unused unsigned flags) noexcept
|
||||
if (!is_idle) {
|
||||
// TODO: can this happen?
|
||||
IdleMonitor::Schedule();
|
||||
return false;
|
||||
SocketMonitor::Cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned idle = (unsigned)mpd_recv_idle(connection, false);
|
||||
@@ -586,7 +587,8 @@ ProxyDatabase::OnSocketReady(gcc_unused unsigned flags) noexcept
|
||||
idle_received |= idle;
|
||||
is_idle = false;
|
||||
IdleMonitor::Schedule();
|
||||
return false;
|
||||
SocketMonitor::Cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
@@ -39,8 +39,8 @@ InitHybridDsdDecoder(const ConfigBlock &block)
|
||||
without a DSD DAC, the PCM (=ALAC) part of the file is
|
||||
better */
|
||||
if (block.GetBlockParam("enabled") == nullptr) {
|
||||
LogInfo(hybrid_dsd_domain,
|
||||
"The Hybrid DSD decoder is disabled because it was not explicitly enabled");
|
||||
LogDebug(hybrid_dsd_domain,
|
||||
"The Hybrid DSD decoder is disabled because it was not explicitly enabled");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -110,15 +110,9 @@ BufferedSocket::OnSocketReady(unsigned flags) noexcept
|
||||
if (flags & READ) {
|
||||
assert(!input.IsFull());
|
||||
|
||||
if (!ReadToBuffer())
|
||||
if (!ReadToBuffer() || !ResumeInput())
|
||||
return false;
|
||||
|
||||
if (!ResumeInput())
|
||||
/* we must return "true" here or
|
||||
SocketMonitor::Dispatch() will call
|
||||
Cancel() on a freed object */
|
||||
return true;
|
||||
|
||||
if (!input.IsFull())
|
||||
ScheduleRead();
|
||||
}
|
||||
|
@@ -46,6 +46,11 @@ public:
|
||||
using SocketMonitor::Close;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @return the number of bytes read from the socket, 0 if the
|
||||
* socket isn't ready for reading, -1 on error (the socket has
|
||||
* been closed and probably destructed)
|
||||
*/
|
||||
ssize_t DirectRead(void *data, size_t length) noexcept;
|
||||
|
||||
/**
|
||||
|
@@ -45,6 +45,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @return the number of bytes written to the socket, 0 if the
|
||||
* socket isn't ready for writing, -1 on error (the socket has
|
||||
* been closed and probably destructed)
|
||||
*/
|
||||
ssize_t DirectWrite(const void *data, size_t length) noexcept;
|
||||
|
||||
protected:
|
||||
|
@@ -392,16 +392,23 @@ ServerSocket::AddPath(AllocatedPath &&path)
|
||||
#else /* !HAVE_UN */
|
||||
(void)path;
|
||||
|
||||
throw std::runtime_error("UNIX domain socket support is disabled");
|
||||
throw std::runtime_error("Local socket support is disabled");
|
||||
#endif /* !HAVE_UN */
|
||||
}
|
||||
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
void
|
||||
ServerSocket::AddAbstract(const char *name)
|
||||
{
|
||||
#if !defined(__linux__)
|
||||
(void)name;
|
||||
|
||||
throw std::runtime_error("Abstract sockets are only available on Linux");
|
||||
#elif !defined(HAVE_UN)
|
||||
(void)name;
|
||||
|
||||
throw std::runtime_error("Local socket support is disabled");
|
||||
#else
|
||||
assert(name != nullptr);
|
||||
assert(*name == '@');
|
||||
|
||||
@@ -409,6 +416,5 @@ ServerSocket::AddAbstract(const char *name)
|
||||
address.SetLocal(name);
|
||||
|
||||
AddAddress(std::move(address));
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@@ -90,7 +90,7 @@ public:
|
||||
void AddHost(const char *hostname, unsigned port);
|
||||
|
||||
/**
|
||||
* Add a listener on a Unix domain socket.
|
||||
* Add a listener on a local socket.
|
||||
*
|
||||
* Throws #std::runtime_error on error.
|
||||
*
|
||||
@@ -99,7 +99,6 @@ public:
|
||||
*/
|
||||
void AddPath(AllocatedPath &&path);
|
||||
|
||||
#ifdef __linux__
|
||||
/**
|
||||
* Add a listener on an abstract local socket (Linux specific).
|
||||
*
|
||||
@@ -109,7 +108,6 @@ public:
|
||||
* instead of a null byte
|
||||
*/
|
||||
void AddAbstract(const char *name);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Add a socket descriptor that is accepting connections. After this
|
||||
|
@@ -33,8 +33,8 @@ SocketMonitor::Dispatch(unsigned flags) noexcept
|
||||
{
|
||||
flags &= GetScheduledFlags();
|
||||
|
||||
if (flags != 0 && !OnSocketReady(flags) && IsDefined())
|
||||
Cancel();
|
||||
if (flags != 0)
|
||||
OnSocketReady(flags);
|
||||
}
|
||||
|
||||
SocketMonitor::~SocketMonitor() noexcept
|
||||
|
@@ -65,7 +65,7 @@ public:
|
||||
|
||||
/**
|
||||
* Flush pending data and return it. This should be called
|
||||
* repepatedly until it returns nullptr.
|
||||
* repeatedly until it returns nullptr.
|
||||
*/
|
||||
virtual ConstBuffer<void> Flush();
|
||||
};
|
||||
|
@@ -56,6 +56,7 @@ public:
|
||||
}
|
||||
|
||||
ConstBuffer<void> FilterPCM(ConstBuffer<void> src) override;
|
||||
ConstBuffer<void> Flush() override;
|
||||
};
|
||||
|
||||
class PreparedAutoConvertFilter final : public PreparedFilter {
|
||||
@@ -104,6 +105,18 @@ AutoConvertFilter::FilterPCM(ConstBuffer<void> src)
|
||||
return filter->FilterPCM(src);
|
||||
}
|
||||
|
||||
ConstBuffer<void>
|
||||
AutoConvertFilter::Flush()
|
||||
{
|
||||
if (convert != nullptr) {
|
||||
auto result = convert->Flush();
|
||||
if (!result.IsNull())
|
||||
return filter->FilterPCM(result);
|
||||
}
|
||||
|
||||
return filter->Flush();
|
||||
}
|
||||
|
||||
std::unique_ptr<PreparedFilter>
|
||||
autoconvert_filter_new(std::unique_ptr<PreparedFilter> filter) noexcept
|
||||
{
|
||||
|
3
src/haiku/add_resources.sh
Executable file
3
src/haiku/add_resources.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
cp "$2" "$1" && xres -o "$1" -- "$3" && mimeset -f "$1" || (rm -f "$1"; exit 1)
|
@@ -1,18 +1,26 @@
|
||||
rc = meson.find_program('rc')
|
||||
xres = meson.find_program('xres')
|
||||
haiku_conf = configuration_data()
|
||||
haiku_conf.set('VERSION', meson.project_version())
|
||||
|
||||
splitted_version = meson.project_version().split('~')[0].split('.')
|
||||
haiku_conf.set('VERSION_MAJOR', splitted_version[0])
|
||||
haiku_conf.set('VERSION_MINOR', splitted_version.get(1, '0'))
|
||||
haiku_conf.set('VERSION_REVISION', splitted_version.get(2, '0'))
|
||||
haiku_conf.set('VERSION_EXTRA', splitted_version.get(3, '0'))
|
||||
|
||||
mpd_rdef = configure_file(
|
||||
input: 'mpd.rdef.in',
|
||||
output: 'mpd.rdef',
|
||||
configuration: haiku_conf,
|
||||
)
|
||||
|
||||
rc = find_program('rc')
|
||||
xres = find_program('xres')
|
||||
|
||||
rsrc = custom_target(
|
||||
'mpd.rsrc',
|
||||
output: 'mpd.rsrc',
|
||||
input: 'mpd.rdef',
|
||||
input: mpd_rdef,
|
||||
command: [rc, '-o', '@OUTPUT@', '@INPUT@'],
|
||||
)
|
||||
|
||||
custom_target(
|
||||
'mpd.rsrc',
|
||||
output: 'mpd',
|
||||
input: [mpd, rsrc],
|
||||
command: [xres, '-o', '@OUTPUT@', '--', '@INPUT@'],
|
||||
install: true,
|
||||
install_dir: get_option('bindir'),
|
||||
)
|
||||
addres = files('add_resources.sh')
|
||||
|
@@ -2,7 +2,15 @@ resource app_signature "application/x-vnd.MusicPD";
|
||||
|
||||
resource app_flags B_BACKGROUND_APP;
|
||||
|
||||
// TODO: resource app_version {};
|
||||
resource app_version {
|
||||
major = @VERSION_MAJOR@,
|
||||
middle = @VERSION_MINOR@,
|
||||
minor = @VERSION_REVISION@,
|
||||
variety = B_APPV_ALPHA,
|
||||
internal = @VERSION_EXTRA@,
|
||||
short_info = "Music Player Daemon @VERSION@",
|
||||
long_info = "Music Player Daemon @VERSION@ ©The Music Player Daemon Project"
|
||||
};
|
||||
|
||||
resource vector_icon {
|
||||
$"6E6369661F050102031604BEE29BBEC5403EC540BEE29B4A10004A10000001C6"
|
@@ -59,6 +59,9 @@ BufferedInputStream::~BufferedInputStream() noexcept
|
||||
void
|
||||
BufferedInputStream::Check()
|
||||
{
|
||||
if (read_error)
|
||||
std::rethrow_exception(read_error);
|
||||
|
||||
if (input)
|
||||
input->Check();
|
||||
}
|
||||
@@ -101,7 +104,7 @@ BufferedInputStream::IsEOF() noexcept
|
||||
bool
|
||||
BufferedInputStream::IsAvailable() noexcept
|
||||
{
|
||||
return IsEOF() || buffer.Read(offset).HasData();
|
||||
return IsEOF() || buffer.Read(offset).HasData() || read_error;
|
||||
}
|
||||
|
||||
size_t
|
||||
@@ -164,6 +167,32 @@ BufferedInputStream::RunThread() noexcept
|
||||
idle = false;
|
||||
seek = false;
|
||||
client_cond.signal();
|
||||
} else if (!idle && !read_error &&
|
||||
offset != input->GetOffset() &&
|
||||
!IsAvailable()) {
|
||||
/* a past Seek() call was a no-op because data
|
||||
was already available at that position, but
|
||||
now we've reached a new position where
|
||||
there is no more data in the buffer, and
|
||||
our input is reading somewhere else (maybe
|
||||
stuck at the end of the file); to find a
|
||||
way out, we now seek our input to our
|
||||
reading position to be able to fill our
|
||||
buffer */
|
||||
|
||||
try {
|
||||
input->Seek(offset);
|
||||
} catch (...) {
|
||||
/* this is really a seek error, but we
|
||||
register it as a read_error,
|
||||
because seek_error is only checked
|
||||
by Seek(), and at our frontend (our
|
||||
own InputStream interface) is in
|
||||
"read" mode */
|
||||
read_error = std::current_exception();
|
||||
client_cond.signal();
|
||||
InvokeOnAvailable();
|
||||
}
|
||||
} else if (!idle && !read_error &&
|
||||
input->IsAvailable() && !input->IsEOF()) {
|
||||
const auto read_offset = input->GetOffset();
|
||||
|
@@ -306,9 +306,8 @@ input_curl_init(EventLoop &event_loop, const ConfigBlock &block)
|
||||
{
|
||||
try {
|
||||
curl_init = new CurlInit(event_loop);
|
||||
} catch (const std::runtime_error &e) {
|
||||
LogError(e);
|
||||
throw PluginUnavailable(e.what());
|
||||
} catch (...) {
|
||||
std::throw_with_nested(PluginUnavailable("CURL initialization failed"));
|
||||
}
|
||||
|
||||
const auto version_info = curl_version_info(CURLVERSION_FIRST);
|
||||
|
@@ -22,14 +22,13 @@
|
||||
#include "lib/smbclient/Mutex.hxx"
|
||||
#include "../InputStream.hxx"
|
||||
#include "../InputPlugin.hxx"
|
||||
#include "../MaybeBufferedInputStream.hxx"
|
||||
#include "PluginUnavailable.hxx"
|
||||
#include "system/Error.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
|
||||
#include <libsmbclient.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
class SmbclientInputStream final : public InputStream {
|
||||
SMBCCTX *ctx;
|
||||
int fd;
|
||||
@@ -72,9 +71,8 @@ input_smbclient_init(EventLoop &, const ConfigBlock &)
|
||||
{
|
||||
try {
|
||||
SmbclientInit();
|
||||
} catch (const std::runtime_error &e) {
|
||||
// TODO: use std::throw_with_nested()?
|
||||
throw PluginUnavailable(e.what());
|
||||
} catch (...) {
|
||||
std::throw_with_nested(PluginUnavailable("libsmbclient initialization failed"));
|
||||
}
|
||||
|
||||
// TODO: create one global SMBCCTX here?
|
||||
@@ -115,8 +113,9 @@ input_smbclient_open(const char *uri,
|
||||
throw MakeErrno(e, "smbc_fstat() failed");
|
||||
}
|
||||
|
||||
return std::make_unique<SmbclientInputStream>(uri, mutex,
|
||||
ctx, fd, st);
|
||||
return std::make_unique<MaybeBufferedInputStream>
|
||||
(std::make_unique<SmbclientInputStream>(uri, mutex,
|
||||
ctx, fd, st));
|
||||
}
|
||||
|
||||
size_t
|
||||
|
@@ -1,4 +1,17 @@
|
||||
# Since version 0.49.0 Meson has native libgcrypt dependency support, which has
|
||||
# the advantage over find_library() as it uses libgcrypt-config to query the
|
||||
# required linker flags.
|
||||
# However, we still need to use find_library() first, to prevent Meson
|
||||
# falsly assuming a target libgcrypt is available in case there is no
|
||||
# libgcrypt-config entry in the cross file and libgcrypt is installed on the
|
||||
# host. In this case, Meson will falsly use the native libgcrypt-config and
|
||||
# will falsly assume it has found the gcrypt library for the target.
|
||||
#
|
||||
# See: https://github.com/MusicPlayerDaemon/MPD/pull/495
|
||||
gcrypt_dep = c_compiler.find_library('gcrypt', required: get_option('qobuz'))
|
||||
if gcrypt_dep.found()
|
||||
gcrypt_dep = dependency('libgcrypt')
|
||||
endif
|
||||
if not gcrypt_dep.found()
|
||||
subdir_done()
|
||||
endif
|
||||
|
@@ -150,8 +150,6 @@ ReadServers(NeighborExplorer::List &list, int fd)
|
||||
smbc_dirent *e;
|
||||
while ((e = smbc_readdir(fd)) != nullptr)
|
||||
ReadEntry(list, *e);
|
||||
|
||||
smbc_closedir(fd);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@@ -168,7 +168,7 @@ public:
|
||||
}
|
||||
|
||||
constexpr operator SocketAddress() const noexcept {
|
||||
return SocketAddress((const struct sockaddr *)&address,
|
||||
return SocketAddress((const struct sockaddr *)(const void *)&address,
|
||||
sizeof(address));
|
||||
}
|
||||
|
||||
|
@@ -135,7 +135,7 @@ public:
|
||||
}
|
||||
|
||||
constexpr operator SocketAddress() const noexcept {
|
||||
return SocketAddress((const struct sockaddr *)&address,
|
||||
return SocketAddress((const struct sockaddr *)(const void *)&address,
|
||||
sizeof(address));
|
||||
}
|
||||
|
||||
|
@@ -35,7 +35,7 @@ socket_bind_listen(int domain, int type, int protocol,
|
||||
throw MakeSocketError("Failed to create socket");
|
||||
|
||||
#ifdef HAVE_UN
|
||||
if (domain == AF_UNIX) {
|
||||
if (domain == AF_LOCAL) {
|
||||
/* Prevent access until right permissions are set */
|
||||
fchmod(fd.Get(), 0);
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ class SocketAddress;
|
||||
/**
|
||||
* Creates a socket listening on the specified address. This is a
|
||||
* shortcut for socket(), bind() and listen().
|
||||
* When a unix socket is created (domain == AF_UNIX), its
|
||||
* When a local socket is created (domain == AF_LOCAL), its
|
||||
* permissions will be stripped down to prevent unauthorized
|
||||
* access. The caller is responsible to apply proper permissions
|
||||
* at a later point.
|
||||
|
@@ -106,7 +106,7 @@ ToString(SocketAddress address) noexcept
|
||||
{
|
||||
#ifdef HAVE_UN
|
||||
if (address.GetFamily() == AF_LOCAL)
|
||||
/* return path of UNIX domain sockets */
|
||||
/* return path of local socket */
|
||||
return LocalAddressToString(*(const sockaddr_un *)address.GetAddress(),
|
||||
address.GetSize());
|
||||
#endif
|
||||
|
@@ -140,9 +140,6 @@ HaikuOutput::Close() noexcept
|
||||
|
||||
HaikuOutput::~HaikuOutput()
|
||||
{
|
||||
delete_sem(new_buffer);
|
||||
delete_sem(buffer_done);
|
||||
|
||||
finalize_application();
|
||||
}
|
||||
|
||||
|
@@ -154,7 +154,7 @@ HttpdClient::SendResponse() noexcept
|
||||
FormatWarning(httpd_output_domain,
|
||||
"failed to write to client: %s",
|
||||
(const char *)msg);
|
||||
Close();
|
||||
LockClose();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -428,6 +428,7 @@ void
|
||||
HttpdClient::OnSocketError(std::exception_ptr ep) noexcept
|
||||
{
|
||||
LogError(ep);
|
||||
LockClose();
|
||||
}
|
||||
|
||||
void
|
||||
|
@@ -142,6 +142,8 @@ public:
|
||||
|
||||
/**
|
||||
* Frees the client and removes it from the server's client list.
|
||||
*
|
||||
* Caller must lock the mutex.
|
||||
*/
|
||||
void Close() noexcept;
|
||||
|
||||
|
@@ -208,10 +208,15 @@ public:
|
||||
return HasClients();
|
||||
}
|
||||
|
||||
/**
|
||||
* Caller must lock the mutex.
|
||||
*/
|
||||
void AddClient(UniqueSocketDescriptor fd) noexcept;
|
||||
|
||||
/**
|
||||
* Removes a client from the httpd_output.clients linked list.
|
||||
*
|
||||
* Caller must lock the mutex.
|
||||
*/
|
||||
void RemoveClient(HttpdClient &client) noexcept;
|
||||
|
||||
@@ -239,10 +244,14 @@ public:
|
||||
|
||||
/**
|
||||
* Broadcasts data from the encoder to all clients.
|
||||
*
|
||||
* Mutext must not be locked.
|
||||
*/
|
||||
void BroadcastFromEncoder();
|
||||
|
||||
/**
|
||||
* Mutext must not be locked.
|
||||
*
|
||||
* Throws #std::runtime_error on error.
|
||||
*/
|
||||
void EncodeAndPlay(const void *chunk, size_t size);
|
||||
@@ -251,6 +260,9 @@ public:
|
||||
|
||||
size_t Play(const void *chunk, size_t size) override;
|
||||
|
||||
/**
|
||||
* Mutext must not be locked.
|
||||
*/
|
||||
void CancelAllClients() noexcept;
|
||||
|
||||
void Cancel() noexcept override;
|
||||
|
@@ -996,7 +996,7 @@ Player::Run() noexcept
|
||||
}
|
||||
}
|
||||
|
||||
if (dc.IsIdle() && queued && dc.pipe == pipe) {
|
||||
if (dc.IsIdle() && queued) {
|
||||
/* the decoder has finished the current song;
|
||||
make it decode the next song */
|
||||
|
||||
@@ -1058,6 +1058,16 @@ Player::Run() noexcept
|
||||
|
||||
SongBorder();
|
||||
} else if (dc.IsIdle()) {
|
||||
if (queued)
|
||||
/* the decoder has just stopped,
|
||||
between the two IsIdle() checks,
|
||||
probably while UnlockCheckOutputs()
|
||||
left the mutex unlocked; to restart
|
||||
the decoder instead of stopping
|
||||
playback completely, let's re-enter
|
||||
this loop */
|
||||
continue;
|
||||
|
||||
/* check the size of the pipe again, because
|
||||
the decoder thread may have added something
|
||||
since we last checked */
|
||||
|
@@ -50,7 +50,7 @@ protected:
|
||||
/* virtual methods from class SocketMonitor */
|
||||
bool OnSocketReady(gcc_unused unsigned flags) noexcept override {
|
||||
DNSServiceProcessResult(service_ref);
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
[Socket]
|
||||
ListenStream=/run/mpd/socket
|
||||
ListenStream=%t/mpd/socket
|
||||
ListenStream=6600
|
||||
Backlog=5
|
||||
KeepAlive=true
|
||||
|
@@ -3,6 +3,12 @@ if systemd_user_unit_dir == ''
|
||||
systemd_user_unit_dir = join_paths(get_option('prefix'), 'lib', 'systemd', 'user')
|
||||
endif
|
||||
|
||||
# copy the system socket unit to the "user" directory
|
||||
install_data(
|
||||
join_paths('..', 'system', 'mpd.socket'),
|
||||
install_dir: systemd_user_unit_dir,
|
||||
)
|
||||
|
||||
configure_file(
|
||||
input: 'mpd.service.in',
|
||||
output: 'mpd.service',
|
||||
|
@@ -165,8 +165,8 @@ public:
|
||||
return GetCommand();
|
||||
}
|
||||
|
||||
void SubmitReplayGain(const ReplayGainInfo *) {}
|
||||
void SubmitMixRamp(MixRampInfo &&) {}
|
||||
void SubmitReplayGain(const ReplayGainInfo *) override {}
|
||||
void SubmitMixRamp(MixRampInfo &&) override {}
|
||||
};
|
||||
|
||||
void
|
||||
|
@@ -1,7 +1,7 @@
|
||||
windows_conf = configuration_data()
|
||||
windows_conf.set('VERSION', meson.project_version())
|
||||
|
||||
splitted_version = meson.project_version().split('.')
|
||||
splitted_version = meson.project_version().split('~')[0].split('.')
|
||||
windows_conf.set('VERSION_MAJOR', splitted_version[0])
|
||||
windows_conf.set('VERSION_MINOR', splitted_version.get(1, '0'))
|
||||
windows_conf.set('VERSION_REVISION', splitted_version.get(2, '0'))
|
||||
|
Reference in New Issue
Block a user