MusicPlayerDaemon / MPD

Music Player Daemon
https://www.musicpd.org/
GNU General Public License v2.0
2.11k stars 338 forks source link

[Feature request] Satellite mode with NFSv4 #2039

Closed EdwinKM closed 1 month ago

EdwinKM commented 1 month ago

Feature request

My previous bug ticket was closed because of missing log files. Created a new ticket with the requested information.

Even if not on the roadmap the bug/feature ticket can be useful to others. So they know it just will not work. The information is missing in the documentation. NFSv4 is only using one port which makes firewal rules much easier (for people with VLAN setup). So, i will file this as a feature request instead.

At least we can add some disclaimer in the MPD documentation? Does this also mean you will not allow/accept a patch?


Bug report

Describe the bug

I have MPD running in satellite mode, where the music is stored on a NAS (Truenas Scale), and my laptop hosts the MPD instance as the sound source.

In the configuration, I've set the music directory to: music_directory "nfs://san/mnt/tank/var/music_temp"

Everything operates smoothly when I use an NFSv3 share. However, when I switch to NFSv4, MPD fails to play the music.

I've verified that the share works fine and can be mounted: mount -v -o vers=4 -t nfs san:/mnt/tank/var/music_temp /mnt/music_temp

According to the libnfs page, NFSv3 seems to be the default, and you need to specify "?version=4" in the URL. I've tried different variations for "music_directory", but they all fail. Additionally, I couldn't find a property in the default MPD configuration to force v4 mode.

Is this possible?

Expected Behavior

Play music with NFSv3 or NFSv4 share

Actual Behavior

NFSv3 works, NFSv4 does not work

Version

Music Player Daemon 0.23.15 (0.23.15)

Configuration

music_directory "nfs://san/mnt/tank/var/music_temp" log_file "~/mpd.log" log_level "warning" auto_update "no" zeroconf_enabled "no" database { plugin "proxy" host "san" port "9999" } input { plugin "curl" } audio_output { type "pulse" name "Pulse Output" } filesystem_charset "UTF-8"

Log

NFSv3

test@linux-5q3o:~> mpd --no-daemon --stdout --verbose
config_file: loading file /home/test/.config/mpd/mpd.conf
server_socket: bind to '0.0.0.0:6600' failed (continuing anyway, because binding to '[::]:6600' succeeded): Failed to bind socket: Address already in use
libsamplerate: libsamplerate converter 'Fastest Sinc Interpolator'
vorbis: Xiph.Org libVorbis 1.3.7
opus: libopus 1.5.1
sndfile: libsndfile-1.2.2-exp
hybrid_dsd: The Hybrid DSD decoder is disabled because it was not explicitly enabled
input: Input plugin 'qobuz' is not configured: No Qobuz app_id configured
curl: version 8.7.1
curl: with OpenSSL/3.1.4
event: RTIOThread could not get realtime scheduling, continuing anyway: sched_setscheduler failed: Operation not permitted
client: [0] opened from [::1]:32772
client: [0] process command "partition "default""
client: [0] command returned 0
client: [1] opened from [::1]:32782
client: [1] process command "partition "default""
client: [1] command returned 0
client: [1] process command "channels"
client: [1] command returned 0
client: [1] process command "idle"
client: [1] command returned 1
client: [0] process command "replay_gain_mode off"
client: [0] command returned 0
client: [0] process command "stats"
client: [0] command returned 0
client: [0] process command "lsinfo "mpd-client://cantata/2.5.0""
exception: Unsupported URI scheme
client: [0] command returned 3
client: [0] process command "listpartitions"
client: [0] command returned 0
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "stats"
client: [0] command returned 0
client: [0] process command "urlhandlers"
client: [0] command returned 0
client: [0] process command "tagtypes"
client: [0] command returned 0
client: [0] process command "commands"
client: [0] command returned 0
client: [0] process command "playlistinfo"
client: [0] command returned 0
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "outputs"
client: [0] command returned 0
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "replay_gain_status"
client: [0] command returned 0
client: [1] process command "idle"
client: [1] command returned 1
client: [0] process command "lsinfo"
exception: Stored playlists are disabled
client: [0] command returned 0
client: [0] process command "currentsong"
client: [0] command returned 0
client: [0] process command "listplaylists"
exception: Stored playlists are disabled
client: [0] command returned 3
client: [0] process command "listplaylistinfo "[Radio Streams]""
exception: NFS: Lookup of /[Radio Streams] failed with NFS3ERR_NOENT(-2)
exception: No such playlist
client: [0] command returned 3
client: [0] process command "playlistinfo"
client: [0] command returned 0
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "outputs"
client: [0] command returned 0
client: [0] process command "clear"
client: [0] command returned 0
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command list
client: process command "add "<REDACTED_ARTIST>/<REDACTED_PATH_TO_MP3>.mp3""
client: command returned 0
client: [0] process command list returned 0
client: [0] process command "play 0"
playlist: play 0:"<REDACTED_ARTIST>/<REDACTED_PATH_TO_MP3>.mp3"
client: [0] command returned 0
client: [0] process command "playlistinfo"
client: [0] command returned 0
client: [0] process command "status"
client: [0] command returned 0
client: [1] process command "idle"
client: [1] command returned 1
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "plchangesposid "3""
client: [0] command returned 0
client: [1] process command "idle"
client: [1] command returned 1
client: [0] process command "currentsong"
client: [0] command returned 0
decoder_thread: probing plugin mad
decoder: audio_format=44100:24:2, seekable=true
client: [0] process command "status"
output: OutputThread could not get realtime scheduling, continuing anyway: sched_setscheduler failed: Operation not permitted
output: opened "Pulse Output" (pulse) audio_format=44100:24:2
client: [0] command returned 0
client: [0] process command "replay_gain_status"
client: [0] command returned 0
client: [1] process command "idle"
client: [1] command returned 1
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "plchangesposid "3""
client: [0] command returned 0
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "replay_gain_status"
client: [0] command returned 0
client: [1] process command "idle"
client: [1] command returned 1
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "replay_gain_status"
client: [0] command returned 0
client: [1] process command "idle"
client: [1] command returned 1
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "replay_gain_status"
client: [0] command returned 0
client: [1] process command "idle"
client: [1] command returned 1
^Cplayer: played "<REDACTED_ARTIST>/<REDACTED_PATH_TO_MP3>.mp3"
output: closed "Pulse Output" (pulse)

NFSv4

test@linux-5q3o:~> mpd --no-daemon --stdout --verbose
config_file: loading file /home/test/.config/mpd/mpd.conf
server_socket: bind to '0.0.0.0:6600' failed (continuing anyway, because binding to '[::]:6600' succeeded): Failed to bind socket: Address already in use
libsamplerate: libsamplerate converter 'Fastest Sinc Interpolator'
vorbis: Xiph.Org libVorbis 1.3.7
opus: libopus 1.5.1
sndfile: libsndfile-1.2.2-exp
hybrid_dsd: The Hybrid DSD decoder is disabled because it was not explicitly enabled
input: Input plugin 'qobuz' is not configured: No Qobuz app_id configured
curl: version 8.7.1
curl: with OpenSSL/3.1.4
event: RTIOThread could not get realtime scheduling, continuing anyway: sched_setscheduler failed: Operation not permitted
client: [0] opened from [::1]:51058
client: [0] process command "partition "default""
client: [0] command returned 0
client: [1] opened from [::1]:51064
client: [1] process command "partition "default""
client: [1] command returned 0
client: [1] process command "channels"
client: [1] command returned 0
client: [1] process command "idle"
client: [1] command returned 1
client: [0] process command "replay_gain_mode off"
client: [0] command returned 0
client: [0] process command "stats"
client: [0] command returned 0
client: [0] process command "lsinfo "mpd-client://cantata/2.5.0""
exception: Unsupported URI scheme
client: [0] command returned 3
client: [0] process command "listpartitions"
client: [0] command returned 0
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "stats"
client: [0] command returned 0
client: [0] process command "urlhandlers"
client: [0] command returned 0
client: [0] process command "tagtypes"
client: [0] command returned 0
client: [0] process command "commands"
client: [0] command returned 0
client: [0] process command "playlistinfo"
client: [0] command returned 0
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "outputs"
client: [0] command returned 0
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "replay_gain_status"
client: [0] command returned 0
client: [1] process command "idle"
client: [1] command returned 1
client: [0] process command "lsinfo"
exception: Stored playlists are disabled
client: [0] command returned 0
client: [0] process command "currentsong"
client: [0] command returned 0
client: [0] process command "listplaylists"
exception: Stored playlists are disabled
client: [0] command returned 3
client: [0] process command "listplaylistinfo "[Radio Streams]""
nfs: NFS error on 'san:/mnt/tank/music': nfs_mount_async() failed:
exception: nfs_mount_async() failed:
exception: No such playlist
client: [0] command returned 3
client: [0] process command "playlistinfo"
client: [0] command returned 0
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "outputs"
client: [0] command returned 0
client: [0] process command "clear"
client: [0] command returned 0
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command list
client: process command "add "<REDACTED_ARTIST>/<REDACTED_PATH_TO_MP3>.mp3""
client: command returned 0
client: [0] process command list returned 0
client: [0] process command "play 0"
playlist: play 0:"<REDACTED_ARTIST>/<REDACTED_PATH_TO_MP3>.mp3"
client: [0] command returned 0
client: [0] process command "playlistinfo"
client: [0] command returned 0
client: [0] process command "status"
client: [0] command returned 0
client: [1] process command "idle"
client: [1] command returned 1
client: [0] process command "currentsong"
client: [0] command returned 0
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "plchangesposid "3""
client: [0] command returned 0
client: [1] process command "idle"
client: [1] command returned 1
nfs: NFS error on 'san:/mnt/tank/music': nfs_mount_async() failed:
exception: Failed to decode nfs://san/mnt/tank/music/<REDACTED_ARTIST>/<REDACTED_PATH_TO_MP3>.mp3; nfs_mount_async() failed:
player: played "<REDACTED_ARTIST>/<REDACTED_PATH_TO_MP3>.mp3"
playlist: stop
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "replay_gain_status"
client: [0] command returned 0
client: [1] process command "idle"
client: [1] command returned 1
client: [0] process command "status"
client: [0] command returned 0
MaxKellermann commented 1 month ago

What you now posted is a libnfs error. Can you demonstrate that libnfs can indeed talk NFSv4 to your server?

EdwinKM commented 1 month ago

Maybe difficult, because it is a library. But before try this.

According to the documentation the default for libnfs seems v3

They state to use something like: nfs://127.0.0.1/my?path/?version=4

But if change music_directory "nfs://san/mnt/tank/music" to music_directory "nfs://san/mnt/tank/music/?version=4"

Things break because it is inserted in the middle.

nfs: NFS error on 'san:/mnt/tank/music/?version=4`': nfs_mount_async() failed:
exception: Failed to decode nfs://san/mnt/tank/music/?version=4`/<REDACTED_PATH_TO_MP3>.MP3; nfs_mount_async() failed:

Another solution would be a new specific property in the mpd.conf to force v4 which does call nfs_set_version(nfs, NFS_V4)

But i think a fix in the music_directory itself seems nicer, for example: music_directory "nfs4://san/mnt/tank/music"

MaxKellermann commented 1 month ago

The documentation about the ?version= URL parameter is somewhat misleading - this works only for programs which use the nfs_parse_url_full() function, which MPD does not use, because it did not exist when MPD added libnfs support. But since the libnfs feature has existed for a long time, we can easily drop support for older versions and switch to that new function.

EdwinKM commented 1 month ago

FWIW . Tried some crappy python

If i use configure NFSv3:

If i use configure NFSv4:

script v3

import libnfs

nfs = libnfs.NFS("nfs://192.168.1.38/mnt/tank/iso")
file_list = nfs.listdir('/')
for file_name in file_list:
  print(file_name)

script v4

import libnfs

nfs = libnfs.NFS("nfs://192.168.1.38/mnt/tank/iso/?version=4")
file_list = nfs.listdir('/')
for file_name in file_list:
  print(file_name)
EdwinKM commented 1 month ago

Thanks, fast implemented!

Compiled from source, tested some scenerio's.

It works, but is it possibly to query the NFS server version beforehand? Scenerio 2 (crash) and 4 are incorrectly configured by the user. Especially for scenerio 4 it can be helpful to log something like: "music_directory configured for NFSv3 but i only found NFSv4 instance".

Scenario 01: NFSv3 server + conf: music_directory "nfs://san/mnt/tank/music" => works

Scenario 02: NFSv3 server + conf: music_directory "nfs://san/mnt/tank/music?version=4" => crash

./mpd --no-daemon --stdout --verbose
config_file: loading file /usr/local/etc/mpd.conf
server_socket: bind to '0.0.0.0:6600' failed (continuing anyway, because binding to '[::]:6600' succeeded): Failed to bind socket: Address already in use
libsamplerate: libsamplerate converter "Fastest Sinc Interpolator"
vorbis: Xiph.Org libVorbis 1.3.7
opus: libopus 1.3.1
sndfile: libsndfile-1.2.0
decoder: Decoder plugin "wildmidi" is unavailable: configuration file does not exist: /etc/timidity/timidity.cfg
adplug: adplug 2.3.3
input: Input plugin "qobuz" is not configured: No Qobuz app_id configured
curl: version 7.88.1
curl: with GnuTLS/3.7.9
event: RTIOThread could not get realtime scheduling, continuing anyway: sched_setscheduler failed: Operation not permitted
terminate called after throwing an instance of 'fmt::v9::format_error'
  what():  string pointer is null
Aborted

Scenario 03: NFSv3 server + conf: music_directory "nfs://san/mnt/tank/music?version=3" => works

Scenario 04: NFSv4 server + conf: music_directory "nfs://san/mnt/tank/music" => No crash. See playlist. But does not play

./mpd --no-daemon --stdout --verbose
config_file: loading file /usr/local/etc/mpd.conf
server_socket: bind to '0.0.0.0:6600' failed (continuing anyway, because binding to '[::]:6600' succeeded): Failed to bind socket: Address already in use
libsamplerate: libsamplerate converter "Fastest Sinc Interpolator"
vorbis: Xiph.Org libVorbis 1.3.7
opus: libopus 1.3.1
sndfile: libsndfile-1.2.0
decoder: Decoder plugin "wildmidi" is unavailable: configuration file does not exist: /etc/timidity/timidity.cfg
adplug: adplug 2.3.3
input: Input plugin "qobuz" is not configured: No Qobuz app_id configured
curl: version 7.88.1
curl: with GnuTLS/3.7.9
event: RTIOThread could not get realtime scheduling, continuing anyway: sched_setscheduler failed: Operation not permitted
nfs: NFS error on 'san:/mnt/tank/music': nfs_mount_async() failed: RPC error. Program is not available on san

Scenario 05: NFSv4 server + conf: music_directory "nfs://san/mnt/tank/music?version=4" => works

Scenario 06: NFSv4 server + conf: music_directory "nfs://san/mnt/tank/music?version=3" => Same output as "Scenario 04"

Scenario 07: NFSv3 + NFSv4 server + conf: music_directory "nfs://san/mnt/tank/music" => works (using NFS3)

Scenario 08: NFSv3 + NFSv4 server + conf: music_directory "nfs://san/mnt/tank/music?version=4" => works (using NFS4)

EdwinKM commented 1 month ago

I seem to have a issue i can not reproduce with NFSv3.

NOTE: My satellite MPD server (nas) is never restarted. It is also not using NFS loopback (direct mount of the music) Only my MPD client is manually compiled.

I use Cantata and in the "Library" i have 4 albums:

I start the satellite MPD client and Cantata connects to this client

I start the satellite MPD clientand Cantata connects to this client

I start the satellite MPD client and Cantata connects to this client

So, after the server once entered the "loop" the client works correct the next instance.

Example of the logging when the loop starts

MPD client

client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "clear"
playlist: stop
player: played "<PATH>/<FIRST_ALBUM>/<SONG_FIRST_ALBUM>.mp3"
output: closed "Pulse Output" (pulse)
client: [0] command returned 0
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command list
client: process command "add \"<SECOND_ALBUM>/<SONG_SECOND_ALBUM>.flac\""
client: command returned 0
client: [0] process command list returned 0
client: [0] process command "play 0"
playlist: play 0:"<SECOND_ALBUM>/<SONG_SECOND_ALBUM>.flac"
client: [0] command returned 0
client: [0] process command "currentsong"
client: [0] command returned 0
client: [0] process command "playlistinfo"
client: [0] command returned 0
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "status"
client: [0] command returned 0
client: [0] process command "replay_gain_status"
client: [0] command returned 0
client: [1] process command "idle"
client: [1] command returned 1
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 0"
decoder_thread: probing plugin flac
decoder: audio_format=44100:16:2, seekable=true
output: opened "Pulse Output" (pulse) audio_format=44100:16:2
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 8192"
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 16384"
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 24576"
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 32768"
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 40960"
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 49152"
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 57344"
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 65536"
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 73728"
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 81920"
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 90112"
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 98304"
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 106496"
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 114688"
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 122880"
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 131072"
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 139264"
client: [0] command returned 0
client: [0] process command "albumart \"<SECOND_ALBUM>/\" 147456"
client: [0] command returned 0
...
...

MPD server

client: [30] process command "find base "<SECOND_ALBUM>/<SONG_SECOND_ALBUM>.flac" window 0:4096"
client: [30] command returned 0
client: [30] process command "idle database"
client: [30] command returned 1
client: [30] process command "lsinfo "<SECOND_ALBUM>/""
exception: No such directory
client: [30] command returned 3
client: [30] process command "idle database"
client: [30] command returned 1
client: [30] process command "lsinfo "<SECOND_ALBUM>/""
exception: No such directory
client: [30] command returned 3
client: [30] process command "idle database"
client: [30] command returned 1
...
...
...
MaxKellermann commented 1 month ago

I won't read your new bug reports here. Write a new bug report for each bug you find.