From e0f5402c5f1e0527c5820bf7990106dd27a18a43 Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Thu, 30 Oct 2025 17:21:15 +0200 Subject: [PATCH 01/19] mpd: use lib.meson* functions --- pkgs/by-name/mp/mpd/package.nix | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/pkgs/by-name/mp/mpd/package.nix b/pkgs/by-name/mp/mpd/package.nix index 661f95ed122b..cb398047f587 100644 --- a/pkgs/by-name/mp/mpd/package.nix +++ b/pkgs/by-name/mp/mpd/package.nix @@ -262,17 +262,19 @@ stdenv.mkDerivation (finalAttrs: { ]; mesonFlags = [ - "-Dtest=true" - "-Dmanpages=true" - "-Dhtml_manual=true" + (lib.mesonBool "test" true) + (lib.mesonBool "manpages" true) + (lib.mesonBool "html_manual" true) ] - ++ map (x: "-D${x}=enabled") features_ - ++ map (x: "-D${x}=disabled") (lib.subtractLists features_ knownFeatures) + ++ map (x: lib.mesonEnable x true) features_ + ++ map (x: lib.mesonEnable x false) (lib.subtractLists features_ knownFeatures) ++ lib.optional (builtins.elem "zeroconf" features_) ( - "-Dzeroconf=" + (if stdenv.hostPlatform.isDarwin then "bonjour" else "avahi") + lib.mesonOption "zeroconf" (if stdenv.hostPlatform.isDarwin then "bonjour" else "avahi") ) - ++ lib.optional (builtins.elem "systemd" features_) "-Dsystemd_system_unit_dir=etc/systemd/system" - ++ lib.optional (builtins.elem "qobuz" features_) "-Dnlohmann_json=enabled"; + ++ lib.optional (builtins.elem "systemd" features_) ( + lib.mesonOption "systemd_system_unit_dir" "etc/systemd/system" + ) + ++ lib.optional (builtins.elem "qobuz" features_) (lib.mesonEnable "nlohmann_json" true); passthru.tests.nixos = nixosTests.mpd; From 3fee4e6609791343ad9b60d968a4e13009e81644 Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Mon, 17 Nov 2025 08:58:19 +0200 Subject: [PATCH 02/19] mpd: use --replace-fail in darwin postPatch substitution --- pkgs/by-name/mp/mpd/package.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/by-name/mp/mpd/package.nix b/pkgs/by-name/mp/mpd/package.nix index cb398047f587..ce78c25494c4 100644 --- a/pkgs/by-name/mp/mpd/package.nix +++ b/pkgs/by-name/mp/mpd/package.nix @@ -232,8 +232,8 @@ stdenv.mkDerivation (finalAttrs: { (stdenv.hostPlatform.isDarwin && lib.versionOlder stdenv.hostPlatform.darwinSdkVersion "12.0") '' substituteInPlace src/output/plugins/OSXOutputPlugin.cxx \ - --replace kAudioObjectPropertyElement{Main,Master} \ - --replace kAudioHardwareServiceDeviceProperty_Virtual{Main,Master}Volume + --replace-fail kAudioObjectPropertyElement{Main,Master} \ + --replace-fail kAudioHardwareServiceDeviceProperty_Virtual{Main,Master}Volume '' + lib.optionalString From 35efcb442532612d7df8e8b589f90290569c7441 Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Thu, 30 Oct 2025 15:15:23 +0200 Subject: [PATCH 03/19] mpd: add doronbehar as maintainer --- pkgs/by-name/mp/mpd/package.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/pkgs/by-name/mp/mpd/package.nix b/pkgs/by-name/mp/mpd/package.nix index ce78c25494c4..0620c6e60c71 100644 --- a/pkgs/by-name/mp/mpd/package.nix +++ b/pkgs/by-name/mp/mpd/package.nix @@ -284,6 +284,7 @@ stdenv.mkDerivation (finalAttrs: { license = lib.licenses.gpl2Only; maintainers = with lib.maintainers; [ tobim + doronbehar ]; platforms = lib.platforms.unix; mainProgram = "mpd"; From d8b10b226fd2a2ed67015c90355ac4328b8a79e9 Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Thu, 30 Oct 2025 10:52:57 +0200 Subject: [PATCH 04/19] nixosTests.mpd: refresh example music track The previous track was 404 probably due to changes in the website. --- nixos/tests/mpd.nix | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nixos/tests/mpd.nix b/nixos/tests/mpd.nix index f80c6658c621..c5144c900cb2 100644 --- a/nixos/tests/mpd.nix +++ b/nixos/tests/mpd.nix @@ -1,11 +1,11 @@ { pkgs, lib, ... }: let track = pkgs.fetchurl { - # Sourced from http://freemusicarchive.org/music/Blue_Wave_Theory/Surf_Music_Month_Challenge/Skyhawk_Beach_fade_in + # Sourced from https://freemusicarchive.org/music/Jazz_at_Mladost_Club/Jazz_Night/Blue_bossa/ - name = "Blue_Wave_Theory-Skyhawk_Beach.mp3"; - url = "https://freemusicarchive.org/file/music/ccCommunity/Blue_Wave_Theory/Surf_Music_Month_Challenge/Blue_Wave_Theory_-_04_-_Skyhawk_Beach.mp3"; - hash = "sha256-91VDWwrcP6Cw4rk72VHvZ8RGfRBrpRE8xo/02dcJhHc="; + name = "Blue bossa - Jazz at Miadost Club.mp3"; + url = "https://files.freemusicarchive.org/storage-freemusicarchive-org/music/no_curator/Jazz_at_Mladost_Club/Jazz_Night/Jazz_at_Mladost_Club_-_07_-_Blue_bossa.mp3?download=1&name=Jazz%20at%20Mladost%20Club%20-%20Blue%20bossa.mp3"; + hash = "sha256-cAG4nBuc97J3ZJc9cm/6vWTgnPL/Hfkar7cA3+89rto="; meta.license = lib.licenses.cc-by-sa-40; }; From 7a16232be9c3b31dd7405832df4984c356ab5d9b Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Thu, 30 Oct 2025 10:51:07 +0200 Subject: [PATCH 05/19] nixosTests.mpd: squash defaultCfg into defaultMpdCfg --- nixos/tests/mpd.nix | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/nixos/tests/mpd.nix b/nixos/tests/mpd.nix index c5144c900cb2..347bcff1b829 100644 --- a/nixos/tests/mpd.nix +++ b/nixos/tests/mpd.nix @@ -9,20 +9,11 @@ let meta.license = lib.licenses.cc-by-sa-40; }; - defaultCfg = rec { + defaultMpdCfg = rec { user = "mpd"; group = "mpd"; dataDir = "/var/lib/mpd"; musicDirectory = "${dataDir}/music"; - }; - - defaultMpdCfg = { - inherit (defaultCfg) - dataDir - musicDirectory - user - group - ; enable = true; }; From 97e96b7f45e1762cfe197e1a89ed96c48c099dba Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Thu, 30 Oct 2025 10:52:17 +0200 Subject: [PATCH 06/19] nixosTests.mpd: don't explicitly set default musicDirectory --- nixos/tests/mpd.nix | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/nixos/tests/mpd.nix b/nixos/tests/mpd.nix index 347bcff1b829..9fe1579169d4 100644 --- a/nixos/tests/mpd.nix +++ b/nixos/tests/mpd.nix @@ -9,11 +9,10 @@ let meta.license = lib.licenses.cc-by-sa-40; }; - defaultMpdCfg = rec { + defaultMpdCfg = { user = "mpd"; group = "mpd"; dataDir = "/var/lib/mpd"; - musicDirectory = "${dataDir}/music"; enable = true; }; @@ -21,7 +20,7 @@ let { user, group, - musicDirectory, + dataDir, }: { description = "Sets up the music file(s) for MPD to use."; @@ -29,7 +28,7 @@ let after = [ "mpd.service" ]; wantedBy = [ "default.target" ]; script = '' - cp ${track} ${musicDirectory} + cp ${track} ${dataDir}/music ''; serviceConfig = { User = user; @@ -68,7 +67,7 @@ in } ''; }; - musicService = musicService { inherit (defaultMpdCfg) user group musicDirectory; }; + musicService = musicService { inherit (defaultMpdCfg) user group dataDir; }; }) { networking.firewall.allowedTCPPorts = [ 6600 ]; } ]; @@ -86,7 +85,7 @@ in ''; }; - musicService = musicService { inherit (defaultMpdCfg) user group musicDirectory; }; + musicService = musicService { inherit (defaultMpdCfg) user group dataDir; }; }) { services.pulseaudio = { From 2df4d2030f0312725c168d2c4768d6dec443fe3c Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Thu, 30 Oct 2025 11:39:59 +0200 Subject: [PATCH 07/19] nixosTests.mpd: put mpd.conf files in $out --- nixos/tests/mpd.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nixos/tests/mpd.nix b/nixos/tests/mpd.nix index 9fe1579169d4..a6fce888375f 100644 --- a/nixos/tests/mpd.nix +++ b/nixos/tests/mpd.nix @@ -134,5 +134,8 @@ in # The PulseAudio-based server is configured not to accept external client connections # to perform the following test: client.fail(f"{mpc} -h serverPulseAudio status") + # For inspecting these files + serverALSA.copy_from_vm("/run/mpd/mpd.conf", "ALSA") + serverPulseAudio.copy_from_vm("/run/mpd/mpd.conf", "PulseAudio") ''; } From 623427215d5828b834c2a1a2fd1acb0507a04b4f Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Thu, 30 Oct 2025 14:14:30 +0200 Subject: [PATCH 08/19] nixosTests.mpd: add doronbehar as maintainer --- nixos/tests/mpd.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nixos/tests/mpd.nix b/nixos/tests/mpd.nix index a6fce888375f..77e2065fca43 100644 --- a/nixos/tests/mpd.nix +++ b/nixos/tests/mpd.nix @@ -47,7 +47,10 @@ in { name = "mpd"; meta = { - maintainers = with lib.maintainers; [ emmanuelrosa ]; + maintainers = with lib.maintainers; [ + emmanuelrosa + doronbehar + ]; }; nodes = { From fce8ebf4d1fd79eb7b9289cd31302b63db0e9228 Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Wed, 29 Oct 2025 16:53:16 +0200 Subject: [PATCH 09/19] nixos/mpd: move user, group & dataDir upwards (preparation commit) --- nixos/modules/services/audio/mpd.nix | 46 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix index 904b0a8c60a4..6f1d5d307a20 100644 --- a/nixos/modules/services/audio/mpd.nix +++ b/nixos/modules/services/audio/mpd.nix @@ -80,6 +80,29 @@ in ''; }; + user = lib.mkOption { + type = lib.types.str; + default = name; + description = "User account under which MPD runs."; + }; + + group = lib.mkOption { + type = lib.types.str; + default = name; + description = "Group account under which MPD runs."; + }; + + dataDir = lib.mkOption { + type = lib.types.path; + default = "/var/lib/${name}"; + description = '' + The directory where MPD stores its state, tag cache, playlists etc. If + left as the default value this directory will automatically be created + before the MPD server starts, otherwise the sysadmin is responsible for + ensuring the directory exists with appropriate ownership and permissions. + ''; + }; + musicDirectory = lib.mkOption { type = with lib.types; either path (strMatching "(http|https|nfs|smb)://.+"); default = "${cfg.dataDir}/music"; @@ -115,29 +138,6 @@ in ''; }; - dataDir = lib.mkOption { - type = lib.types.path; - default = "/var/lib/${name}"; - description = '' - The directory where MPD stores its state, tag cache, playlists etc. If - left as the default value this directory will automatically be created - before the MPD server starts, otherwise the sysadmin is responsible for - ensuring the directory exists with appropriate ownership and permissions. - ''; - }; - - user = lib.mkOption { - type = lib.types.str; - default = name; - description = "User account under which MPD runs."; - }; - - group = lib.mkOption { - type = lib.types.str; - default = name; - description = "Group account under which MPD runs."; - }; - network = { listenAddress = lib.mkOption { From 90d162890d712b48a056f8e0c00376390a06aec9 Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Wed, 29 Oct 2025 17:00:11 +0200 Subject: [PATCH 10/19] nixos/mpd: rename some settings to fit upstream's names --- nixos/modules/services/audio/mpd.nix | 58 ++++++++++++++++++---------- nixos/tests/mpd.nix | 2 +- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix index 6f1d5d307a20..6897c9bfb34c 100644 --- a/nixos/modules/services/audio/mpd.nix +++ b/nixos/modules/services/audio/mpd.nix @@ -29,18 +29,18 @@ let # via NixOS' configuration.nix, as this file will be rewritten upon mpd's # restart. - music_directory "${cfg.musicDirectory}" - playlist_directory "${cfg.playlistDirectory}" - ${lib.optionalString (cfg.dbFile != null) '' - db_file "${cfg.dbFile}" + music_directory "${cfg.music_directory}" + playlist_directory "${cfg.playlist_directory}" + ${lib.optionalString (cfg.db_file != null) '' + db_file "${cfg.db_file}" ''} state_file "${cfg.dataDir}/state" sticker_file "${cfg.dataDir}/sticker.sql" ${lib.optionalString ( - cfg.network.listenAddress != "any" - ) ''bind_to_address "${cfg.network.listenAddress}"''} - ${lib.optionalString (cfg.network.port != 6600) ''port "${toString cfg.network.port}"''} + cfg.bind_to_address != "any" + ) ''bind_to_address "${cfg.bind_to_address}"''} + ${lib.optionalString (cfg.port != 6600) ''port "${toString cfg.port}"''} ${lib.optionalString (cfg.fluidsynth) '' decoder { plugin "fluidsynth" @@ -103,7 +103,7 @@ in ''; }; - musicDirectory = lib.mkOption { + music_directory = lib.mkOption { type = with lib.types; either path (strMatching "(http|https|nfs|smb)://.+"); default = "${cfg.dataDir}/music"; defaultText = lib.literalExpression ''"''${dataDir}/music"''; @@ -115,7 +115,7 @@ in ''; }; - playlistDirectory = lib.mkOption { + playlist_directory = lib.mkOption { type = lib.types.path; default = "${cfg.dataDir}/playlists"; defaultText = lib.literalExpression ''"''${dataDir}/playlists"''; @@ -138,9 +138,7 @@ in ''; }; - network = { - - listenAddress = lib.mkOption { + bind_to_address = lib.mkOption { type = lib.types.str; default = "127.0.0.1"; example = "any"; @@ -159,9 +157,7 @@ in ''; }; - }; - - dbFile = lib.mkOption { + db_file = lib.mkOption { type = lib.types.nullOr lib.types.str; default = "${cfg.dataDir}/tag_cache"; defaultText = lib.literalExpression ''"''${dataDir}/tag_cache"''; @@ -235,6 +231,26 @@ in ###### implementation + imports = [ + (lib.mkRenamedOptionModule + [ "services" "mpd" "musicDirectory" ] + [ "services" "mpd" "music_directory" ] + ) + (lib.mkRenamedOptionModule + [ "services" "mpd" "playlistDirectory" ] + [ "services" "mpd" "playlist_directory" ] + ) + (lib.mkRenamedOptionModule [ "services" "mpd" "dbFile" ] [ "services" "mpd" "db_file" ]) + (lib.mkRenamedOptionModule + [ "services" "mpd" "network" "listenAddress" ] + [ "services" "mpd" "bind_to_address" ] + ) + (lib.mkRenamedOptionModule + [ "services" "mpd" "network" "port" ] + [ "services" "mpd" "port" ] + ) + ]; + config = lib.mkIf cfg.enable { # install mpd units @@ -245,12 +261,12 @@ in listenStreams = [ "" # Note: this is needed to override the upstream unit ( - if pkgs.lib.hasPrefix "/" cfg.network.listenAddress then - cfg.network.listenAddress + if pkgs.lib.hasPrefix "/" cfg.bind_to_address then + cfg.bind_to_address else "${ - lib.optionalString (cfg.network.listenAddress != "any") "${cfg.network.listenAddress}:" - }${toString cfg.network.port}" + lib.optionalString (cfg.bind_to_address != "any") "${cfg.bind_to_address}:" + }${toString cfg.port}" ) ]; }; @@ -282,11 +298,11 @@ in StateDirectory = [ ] ++ lib.optionals (cfg.dataDir == "/var/lib/${name}") [ name ] - ++ lib.optionals (cfg.playlistDirectory == "/var/lib/${name}/playlists") [ + ++ lib.optionals (cfg.playlist_directory == "/var/lib/${name}/playlists") [ name "${name}/playlists" ] - ++ lib.optionals (cfg.musicDirectory == "/var/lib/${name}/music") [ + ++ lib.optionals (cfg.music_directory == "/var/lib/${name}/music") [ name "${name}/music" ]; diff --git a/nixos/tests/mpd.nix b/nixos/tests/mpd.nix index 77e2065fca43..4d8cf4cdbb02 100644 --- a/nixos/tests/mpd.nix +++ b/nixos/tests/mpd.nix @@ -61,7 +61,7 @@ in lib.mkMerge [ (mkServer { mpd = defaultMpdCfg // { - network.listenAddress = "any"; + bind_to_address = "any"; extraConfig = '' audio_output { type "alsa" From 5759fff21b3eb19d9cbd74cce205c5629e2cace6 Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Wed, 29 Oct 2025 17:06:37 +0200 Subject: [PATCH 11/19] nixos/mpd: small indentation fix --- nixos/modules/services/audio/mpd.nix | 34 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix index 6897c9bfb34c..0e58ca1e100f 100644 --- a/nixos/modules/services/audio/mpd.nix +++ b/nixos/modules/services/audio/mpd.nix @@ -138,24 +138,24 @@ in ''; }; - bind_to_address = lib.mkOption { - type = lib.types.str; - default = "127.0.0.1"; - example = "any"; - description = '' - The address for the daemon to listen on. - Use `any` to listen on all addresses. - ''; - }; + bind_to_address = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + example = "any"; + description = '' + The address for the daemon to listen on. + Use `any` to listen on all addresses. + ''; + }; - port = lib.mkOption { - type = lib.types.port; - default = 6600; - description = '' - This setting is the TCP port that is desired for the daemon to get assigned - to. - ''; - }; + port = lib.mkOption { + type = lib.types.port; + default = 6600; + description = '' + This setting is the TCP port that is desired for the daemon to get assigned + to. + ''; + }; db_file = lib.mkOption { type = lib.types.nullOr lib.types.str; From 9279e2a51ce64757999531a85de0f2093e9444f2 Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Wed, 29 Oct 2025 17:07:01 +0200 Subject: [PATCH 12/19] nixos/mpd: small db_file type fix --- nixos/modules/services/audio/mpd.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix index 0e58ca1e100f..04941fefa1eb 100644 --- a/nixos/modules/services/audio/mpd.nix +++ b/nixos/modules/services/audio/mpd.nix @@ -158,7 +158,7 @@ in }; db_file = lib.mkOption { - type = lib.types.nullOr lib.types.str; + type = lib.types.nullOr lib.types.path; default = "${cfg.dataDir}/tag_cache"; defaultText = lib.literalExpression ''"''${dataDir}/tag_cache"''; description = '' From eff6e4f42c9397b227fc2a5be7c3088e1cbeee65 Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Mon, 17 Nov 2025 11:45:56 +0200 Subject: [PATCH 13/19] nixos/mpd: allow any URI schema in music_directory There are many more URI schemas possible, and it depends on storage plugins activated etc. --- nixos/modules/services/audio/mpd.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix index 04941fefa1eb..2f1012ae3d85 100644 --- a/nixos/modules/services/audio/mpd.nix +++ b/nixos/modules/services/audio/mpd.nix @@ -104,11 +104,11 @@ in }; music_directory = lib.mkOption { - type = with lib.types; either path (strMatching "(http|https|nfs|smb)://.+"); + type = with lib.types; either path (strMatching "([a-z]+)://.+"); default = "${cfg.dataDir}/music"; defaultText = lib.literalExpression ''"''${dataDir}/music"''; description = '' - The directory or NFS/SMB network share where MPD reads music from. If left + The directory or URI where MPD reads music from. If left as the default value this directory will automatically be created before the MPD server starts, otherwise the sysadmin is responsible for ensuring the directory exists with appropriate ownership and permissions. From c8047f7b83b63de0194e3dc3d4228d387017d2bc Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Wed, 29 Oct 2025 17:14:26 +0200 Subject: [PATCH 14/19] nixos/mpd: always set all options explicitly --- nixos/modules/services/audio/mpd.nix | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix index 2f1012ae3d85..692e52f6788d 100644 --- a/nixos/modules/services/audio/mpd.nix +++ b/nixos/modules/services/audio/mpd.nix @@ -31,16 +31,12 @@ let music_directory "${cfg.music_directory}" playlist_directory "${cfg.playlist_directory}" - ${lib.optionalString (cfg.db_file != null) '' - db_file "${cfg.db_file}" - ''} + db_file "${cfg.db_file}" state_file "${cfg.dataDir}/state" sticker_file "${cfg.dataDir}/sticker.sql" - ${lib.optionalString ( - cfg.bind_to_address != "any" - ) ''bind_to_address "${cfg.bind_to_address}"''} - ${lib.optionalString (cfg.port != 6600) ''port "${toString cfg.port}"''} + bind_to_address "${cfg.bind_to_address}" + port "${toString cfg.port}" ${lib.optionalString (cfg.fluidsynth) '' decoder { plugin "fluidsynth" @@ -158,12 +154,11 @@ in }; db_file = lib.mkOption { - type = lib.types.nullOr lib.types.path; + type = lib.types.path; default = "${cfg.dataDir}/tag_cache"; defaultText = lib.literalExpression ''"''${dataDir}/tag_cache"''; description = '' - The path to MPD's database. If set to `null` the - parameter is omitted from the configuration. + The path to MPD's database. ''; }; From 4b9430a629331d8660d061be5c4f7587a073e82b Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Wed, 29 Oct 2025 17:25:01 +0200 Subject: [PATCH 15/19] nixos/mpd: add openFirewall option --- nixos/modules/services/audio/mpd.nix | 19 +++++++++++++++++++ nixos/tests/mpd.nix | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix index 692e52f6788d..835009f6b081 100644 --- a/nixos/modules/services/audio/mpd.nix +++ b/nixos/modules/services/audio/mpd.nix @@ -99,6 +99,12 @@ in ''; }; + openFirewall = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Open ports in the firewall for mpd."; + }; + music_directory = lib.mkOption { type = with lib.types; either path (strMatching "([a-z]+)://.+"); default = "${cfg.dataDir}/music"; @@ -248,6 +254,17 @@ in config = lib.mkIf cfg.enable { + warnings = + lib.optional + ( + !(builtins.elem cfg.bind_to_address [ + "localhost" + "127.0.0.1" + ]) + && !cfg.openFirewall + ) + "Using '${cfg.bind_to_address}' as services.mpd.bind_to_address without enabling services.mpd.openFirewall, might prevent you from accessing MPD from other clients."; + # install mpd units systemd.packages = [ pkgs.mpd ]; @@ -304,6 +321,8 @@ in }; }; + networking.firewall.allowedTCPPorts = lib.optionals cfg.openFirewall [ cfg.port ]; + users.users = lib.optionalAttrs (cfg.user == name) { ${name} = { inherit uid; diff --git a/nixos/tests/mpd.nix b/nixos/tests/mpd.nix index 4d8cf4cdbb02..571c1428b972 100644 --- a/nixos/tests/mpd.nix +++ b/nixos/tests/mpd.nix @@ -69,10 +69,10 @@ in mixer_type "null" } ''; + openFirewall = true; }; musicService = musicService { inherit (defaultMpdCfg) user group dataDir; }; }) - { networking.firewall.allowedTCPPorts = [ 6600 ]; } ]; serverPulseAudio = From 0d168edb2876014627a56b787e3c75a7ec60ef7c Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Mon, 17 Nov 2025 22:08:29 +0200 Subject: [PATCH 16/19] nixos/mpd: use pkgs.writeTextFile, with a checkPhase --- nixos/modules/services/audio/mpd.nix | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix index 835009f6b081..2f5e8c1a2bcd 100644 --- a/nixos/modules/services/audio/mpd.nix +++ b/nixos/modules/services/audio/mpd.nix @@ -24,7 +24,9 @@ let lib.concatStringsSep "\n" placeholders ); - mpdConf = pkgs.writeText "mpd.conf" '' + mpdConf = pkgs.writeTextFile { + name = "mpd.conf"; + text = '' # This file was automatically generated by NixOS. Edit mpd's configuration # via NixOS' configuration.nix, as this file will be rewritten upon mpd's # restart. @@ -48,6 +50,27 @@ let ${cfg.extraConfig} ''; + derivationArgs = { + expectScript = '' + spawn ${lib.getExe pkgs.buildPackages.mpd} --no-daemon "$env(out)" + expect { + "exception: Error in \"$env(out)\"" { + puts "Config file invalid\n" + exit 1 + } + "exception:" { + exit 0 + } + } + ''; + passAsFile = [ + "expectScript" + ]; + }; + checkPhase = '' + ${lib.getExe pkgs.buildPackages.expect} -f "$expectScriptPath" + ''; + }; in { From 523eaab940353b8236791874dbd42e6b4b35f97f Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Mon, 17 Nov 2025 22:14:06 +0200 Subject: [PATCH 17/19] nixos/mpd: small formatting to mpdConf --- nixos/modules/services/audio/mpd.nix | 38 ++++++++++++++-------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix index 2f5e8c1a2bcd..b57ec9b9f7aa 100644 --- a/nixos/modules/services/audio/mpd.nix +++ b/nixos/modules/services/audio/mpd.nix @@ -27,29 +27,29 @@ let mpdConf = pkgs.writeTextFile { name = "mpd.conf"; text = '' - # This file was automatically generated by NixOS. Edit mpd's configuration - # via NixOS' configuration.nix, as this file will be rewritten upon mpd's - # restart. + # This file was automatically generated by NixOS. Edit mpd's configuration + # via NixOS' configuration.nix, as this file will be rewritten upon mpd's + # restart. - music_directory "${cfg.music_directory}" - playlist_directory "${cfg.playlist_directory}" - db_file "${cfg.db_file}" - state_file "${cfg.dataDir}/state" - sticker_file "${cfg.dataDir}/sticker.sql" + music_directory "${cfg.music_directory}" + playlist_directory "${cfg.playlist_directory}" + db_file "${cfg.db_file}" + state_file "${cfg.dataDir}/state" + sticker_file "${cfg.dataDir}/sticker.sql" - bind_to_address "${cfg.bind_to_address}" - port "${toString cfg.port}" - ${lib.optionalString (cfg.fluidsynth) '' - decoder { - plugin "fluidsynth" - soundfont "${pkgs.soundfont-fluid}/share/soundfonts/FluidR3_GM2-2.sf2" - } - ''} + bind_to_address "${cfg.bind_to_address}" + port "${toString cfg.port}" + ${lib.optionalString (cfg.fluidsynth) '' + decoder { + plugin "fluidsynth" + soundfont "${pkgs.soundfont-fluid}/share/soundfonts/FluidR3_GM2-2.sf2" + } + ''} - ${lib.optionalString (cfg.credentials != [ ]) (credentialsPlaceholder cfg.credentials)} + ${lib.optionalString (cfg.credentials != [ ]) (credentialsPlaceholder cfg.credentials)} - ${cfg.extraConfig} - ''; + ${cfg.extraConfig} + ''; derivationArgs = { expectScript = '' spawn ${lib.getExe pkgs.buildPackages.mpd} --no-daemon "$env(out)" From aed4ee1a79ef1321c2ba5b1efa34481f8ab0c6c6 Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Wed, 29 Oct 2025 17:38:46 +0200 Subject: [PATCH 18/19] nixos/mpd: apply RFC 42 Also use `config.services.mpd.settings` directly in the documentation of mpdscribble. --- nixos/modules/services/audio/mpd.nix | 203 ++++++++++++++----- nixos/modules/services/audio/mpdscribble.nix | 11 +- nixos/modules/services/audio/ympd.nix | 4 +- nixos/tests/mpd.nix | 28 +-- 4 files changed, 169 insertions(+), 77 deletions(-) diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix index b57ec9b9f7aa..af6961692ee0 100644 --- a/nixos/modules/services/audio/mpd.nix +++ b/nixos/modules/services/audio/mpd.nix @@ -12,44 +12,64 @@ let gid = config.ids.gids.mpd; cfg = config.services.mpd; - credentialsPlaceholder = ( - creds: - let - placeholders = ( - lib.imap0 ( - i: c: ''password "{{password-${toString i}}}@${lib.concatStringsSep "," c.permissions}"'' - ) creds - ); - in - lib.concatStringsSep "\n" placeholders - ); - + mkKeyValue = + a: + lib.mapAttrsToList ( + k: v: + k + + " " + + ( + if builtins.isBool v then + # Mainly for https://mpd.readthedocs.io/en/stable/user.html#zeroconf + "\"" + (lib.boolToYesNo v) + "\"" + else + "\"" + (toString v) + "\"" + ) + ) a; + nonBlockSettings = lib.filterAttrs (n: v: !(builtins.isAttrs v || builtins.isList v)) cfg.settings; + pureBlockSettings = builtins.removeAttrs cfg.settings (builtins.attrNames nonBlockSettings); + blocks = + pureBlockSettings + // lib.optionalAttrs cfg.fluidsynth { + decoder = (pureBlockSettings.decoder or [ ]) ++ [ + { + plugin = "fluidsynth"; + soundfont = "${pkgs.soundfont-fluid}/share/soundfonts/FluidR3_GM2-2.sf2"; + } + ]; + }; + processSingleBlock = + n: v: + [ + (n + " {") + ] + # Add indentation, for better readability + ++ (map (l: " " + l) (mkKeyValue v)) + ++ [ "}" ]; mpdConf = pkgs.writeTextFile { name = "mpd.conf"; text = '' # This file was automatically generated by NixOS. Edit mpd's configuration # via NixOS' configuration.nix, as this file will be rewritten upon mpd's # restart. - - music_directory "${cfg.music_directory}" - playlist_directory "${cfg.playlist_directory}" - db_file "${cfg.db_file}" - state_file "${cfg.dataDir}/state" - sticker_file "${cfg.dataDir}/sticker.sql" - - bind_to_address "${cfg.bind_to_address}" - port "${toString cfg.port}" - ${lib.optionalString (cfg.fluidsynth) '' - decoder { - plugin "fluidsynth" - soundfont "${pkgs.soundfont-fluid}/share/soundfonts/FluidR3_GM2-2.sf2" + '' + + lib.concatStringsSep "\n" ( + mkKeyValue ( + { + state_file = "${cfg.dataDir}/state"; + sticker_file = "${cfg.dataDir}/sticker.sql"; } - ''} - - ${lib.optionalString (cfg.credentials != [ ]) (credentialsPlaceholder cfg.credentials)} - - ${cfg.extraConfig} - ''; + // nonBlockSettings + ) + ++ lib.flatten ( + lib.mapAttrsToList ( + n: v: if builtins.isList v then (map (b: processSingleBlock n b) v) else (processSingleBlock n v) + ) blocks + ) + ++ lib.imap0 ( + i: a: "password \"{{password-${toString i}}}@${lib.concatStringsSep "," a.permissions}\"" + ) cfg.credentials + ); derivationArgs = { expectScript = '' spawn ${lib.getExe pkgs.buildPackages.mpd} --no-daemon "$env(out)" @@ -128,6 +148,31 @@ in description = "Open ports in the firewall for mpd."; }; + settings = lib.mkOption { + type = lib.types.submodule { + freeformType = + let + inherit (lib.types) + oneOf + attrsOf + listOf + str + int + bool + path + ; + atomType = oneOf [ + str + int + bool + path + ]; + in + attrsOf (oneOf [ + atomType + (listOf (attrsOf atomType)) + ]); + options = { music_directory = lib.mkOption { type = with lib.types; either path (strMatching "([a-z]+)://.+"); default = "${cfg.dataDir}/music"; @@ -152,17 +197,6 @@ in ''; }; - extraConfig = lib.mkOption { - type = lib.types.lines; - default = ""; - description = '' - Extra directives added to to the end of MPD's configuration file, - mpd.conf. Basic configuration like file location and uid/gid - is added automatically to the beginning of the file. For available - options see {manpage}`mpd.conf(5)`. - ''; - }; - bind_to_address = lib.mkOption { type = lib.types.str; default = "127.0.0.1"; @@ -190,6 +224,55 @@ in The path to MPD's database. ''; }; + }; + }; + default = { }; + description = '' + Configuration for MPD. MPD supports key-value like blocks for settings + like `audio_output` and `neighbor`. Some of these blocks can be + specified multiple times, so the following configuration: + + ```txt + audio_output { + device "iec958:CARD=Intel,DEV=0" + mixer_control "PCM" + name "My specific ALSA output" + type "alsa" + } + audio_output { + mixer_type "null" + name "ALSA Null" + type "alsa" + } + audio_output { + name "The Pulse" + type "pulse" + } + ``` + + Can be inserted with: + + ```nix + audio_output = [ + { + type = "alsa"; + name = "My specific ALSA output"; + device = "iec958:CARD=Intel,DEV=0"; + mixer_control = "PCM"; + } + { + type = "alsa"; + name = "ALSA Null"; + mixer_type = "null"; + } + { + type = "pulse"; + name = "The Pulse"; + } + ]; + ``` + ''; + }; credentials = lib.mkOption { type = lib.types.listOf ( @@ -246,7 +329,7 @@ in type = lib.types.bool; default = false; description = '' - If set, add fluidsynth soundfont and configure the plugin. + If set, add fluidsynth soundfont `decoder` block. ''; }; }; @@ -258,20 +341,28 @@ in imports = [ (lib.mkRenamedOptionModule [ "services" "mpd" "musicDirectory" ] - [ "services" "mpd" "music_directory" ] + [ "services" "mpd" "settings" "music_directory" ] ) (lib.mkRenamedOptionModule [ "services" "mpd" "playlistDirectory" ] - [ "services" "mpd" "playlist_directory" ] + [ "services" "mpd" "settings" "playlist_directory" ] ) - (lib.mkRenamedOptionModule [ "services" "mpd" "dbFile" ] [ "services" "mpd" "db_file" ]) + (lib.mkRenamedOptionModule [ "services" "mpd" "dbFile" ] [ "services" "mpd" "settings" "db_file" ]) (lib.mkRenamedOptionModule [ "services" "mpd" "network" "listenAddress" ] - [ "services" "mpd" "bind_to_address" ] + [ "services" "mpd" "settings" "bind_to_address" ] ) (lib.mkRenamedOptionModule [ "services" "mpd" "network" "port" ] - [ "services" "mpd" "port" ] + [ "services" "mpd" "settings" "port" ] + ) + (lib.mkRemovedOptionModule + [ + "services" + "mpd" + "extraConfig" + ] + "services.mpd.extraConfig was replaced by the declarative services.mpd.settings option, per RFC42." ) ]; @@ -286,7 +377,7 @@ in ]) && !cfg.openFirewall ) - "Using '${cfg.bind_to_address}' as services.mpd.bind_to_address without enabling services.mpd.openFirewall, might prevent you from accessing MPD from other clients."; + "Using '${cfg.bind_to_address}' as services.mpd.settings.bind_to_address without enabling services.mpd.openFirewall, might prevent you from accessing MPD from other clients."; # install mpd units systemd.packages = [ pkgs.mpd ]; @@ -296,12 +387,12 @@ in listenStreams = [ "" # Note: this is needed to override the upstream unit ( - if pkgs.lib.hasPrefix "/" cfg.bind_to_address then - cfg.bind_to_address + if pkgs.lib.hasPrefix "/" cfg.settings.bind_to_address then + cfg.settings.bind_to_address else "${ - lib.optionalString (cfg.bind_to_address != "any") "${cfg.bind_to_address}:" - }${toString cfg.port}" + lib.optionalString (cfg.settings.bind_to_address != "any") "${cfg.settings.bind_to_address}:" + }${toString cfg.settings.port}" ) ]; }; @@ -333,18 +424,18 @@ in StateDirectory = [ ] ++ lib.optionals (cfg.dataDir == "/var/lib/${name}") [ name ] - ++ lib.optionals (cfg.playlist_directory == "/var/lib/${name}/playlists") [ + ++ lib.optionals (cfg.settings.playlist_directory == "/var/lib/${name}/playlists") [ name "${name}/playlists" ] - ++ lib.optionals (cfg.music_directory == "/var/lib/${name}/music") [ + ++ lib.optionals (cfg.settings.music_directory == "/var/lib/${name}/music") [ name "${name}/music" ]; }; }; - networking.firewall.allowedTCPPorts = lib.optionals cfg.openFirewall [ cfg.port ]; + networking.firewall.allowedTCPPorts = lib.optionals cfg.openFirewall [ cfg.settings.port ]; users.users = lib.optionalAttrs (cfg.user == name) { ${name} = { diff --git a/nixos/modules/services/audio/mpdscribble.nix b/nixos/modules/services/audio/mpdscribble.nix index 39dab7105968..cd874f000b3a 100644 --- a/nixos/modules/services/audio/mpdscribble.nix +++ b/nixos/modules/services/audio/mpdscribble.nix @@ -8,7 +8,6 @@ let cfg = config.services.mpdscribble; mpdCfg = config.services.mpd; - mpdOpt = options.services.mpd; endpointUrls = { "last.fm" = "http://post.audioscrobbler.com"; @@ -114,11 +113,11 @@ in host = lib.mkOption { default = ( - if mpdCfg.network.listenAddress != "any" then mpdCfg.network.listenAddress else "localhost" + if mpdCfg.settings.bind_to_address != "any" then mpdCfg.settings.bind_to_address else "localhost" ); defaultText = lib.literalExpression '' - if config.${mpdOpt.network.listenAddress} != "any" - then config.${mpdOpt.network.listenAddress} + if config.services.mpd.settings.bind_to_address != "any" + then config.services.mpd.settings.bind_to_address else "localhost" ''; type = lib.types.str; @@ -148,8 +147,8 @@ in }; port = lib.mkOption { - default = mpdCfg.network.port; - defaultText = lib.literalExpression "config.${mpdOpt.network.port}"; + default = mpdCfg.settings.port; + defaultText = lib.literalExpression "config.services.mpd.settings.port"; type = lib.types.port; description = '' Port for the mpdscribble daemon to search for a mpd daemon on. diff --git a/nixos/modules/services/audio/ympd.nix b/nixos/modules/services/audio/ympd.nix index 12672eb935f1..0999291ca0da 100644 --- a/nixos/modules/services/audio/ympd.nix +++ b/nixos/modules/services/audio/ympd.nix @@ -33,8 +33,8 @@ in port = lib.mkOption { type = lib.types.port; - default = config.services.mpd.network.port; - defaultText = lib.literalExpression "config.services.mpd.network.port"; + default = config.services.mpd.settings.port; + defaultText = lib.literalExpression "config.services.mpd.settings.port"; description = "The port where MPD is listening."; example = 6600; }; diff --git a/nixos/tests/mpd.nix b/nixos/tests/mpd.nix index 571c1428b972..36337bfbb4de 100644 --- a/nixos/tests/mpd.nix +++ b/nixos/tests/mpd.nix @@ -61,14 +61,16 @@ in lib.mkMerge [ (mkServer { mpd = defaultMpdCfg // { - bind_to_address = "any"; - extraConfig = '' - audio_output { - type "alsa" - name "ALSA" - mixer_type "null" - } - ''; + settings = { + bind_to_address = "any"; + audio_output = [ + { + type = "alsa"; + name = "ALSA"; + mixer_type = "null"; + } + ]; + }; openFirewall = true; }; musicService = musicService { inherit (defaultMpdCfg) user group dataDir; }; @@ -80,12 +82,12 @@ in lib.mkMerge [ (mkServer { mpd = defaultMpdCfg // { - extraConfig = '' - audio_output { - type "pulse" - name "The Pulse" + settings.audio_output = [ + { + type = "pulse"; + name = "The Pulse"; } - ''; + ]; }; musicService = musicService { inherit (defaultMpdCfg) user group dataDir; }; From 53943b006f4a493fdeb83f4f8d6b0ec4895dfbc3 Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Mon, 17 Nov 2025 23:40:36 +0200 Subject: [PATCH 19/19] nixos/mpd: nixfmt --- nixos/modules/services/audio/mpd.nix | 94 ++++++++++++++-------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix index af6961692ee0..1a58732ca944 100644 --- a/nixos/modules/services/audio/mpd.nix +++ b/nixos/modules/services/audio/mpd.nix @@ -173,57 +173,57 @@ in (listOf (attrsOf atomType)) ]); options = { - music_directory = lib.mkOption { - type = with lib.types; either path (strMatching "([a-z]+)://.+"); - default = "${cfg.dataDir}/music"; - defaultText = lib.literalExpression ''"''${dataDir}/music"''; - description = '' - The directory or URI where MPD reads music from. If left - as the default value this directory will automatically be created before - the MPD server starts, otherwise the sysadmin is responsible for ensuring - the directory exists with appropriate ownership and permissions. - ''; - }; + music_directory = lib.mkOption { + type = with lib.types; either path (strMatching "([a-z]+)://.+"); + default = "${cfg.dataDir}/music"; + defaultText = lib.literalExpression ''"''${dataDir}/music"''; + description = '' + The directory or URI where MPD reads music from. If left + as the default value this directory will automatically be created before + the MPD server starts, otherwise the sysadmin is responsible for ensuring + the directory exists with appropriate ownership and permissions. + ''; + }; - playlist_directory = lib.mkOption { - type = lib.types.path; - default = "${cfg.dataDir}/playlists"; - defaultText = lib.literalExpression ''"''${dataDir}/playlists"''; - description = '' - The directory where MPD stores playlists. If left as the default value - this directory will automatically be created before the MPD server starts, - otherwise the sysadmin is responsible for ensuring the directory exists - with appropriate ownership and permissions. - ''; - }; + playlist_directory = lib.mkOption { + type = lib.types.path; + default = "${cfg.dataDir}/playlists"; + defaultText = lib.literalExpression ''"''${dataDir}/playlists"''; + description = '' + The directory where MPD stores playlists. If left as the default value + this directory will automatically be created before the MPD server starts, + otherwise the sysadmin is responsible for ensuring the directory exists + with appropriate ownership and permissions. + ''; + }; - bind_to_address = lib.mkOption { - type = lib.types.str; - default = "127.0.0.1"; - example = "any"; - description = '' - The address for the daemon to listen on. - Use `any` to listen on all addresses. - ''; - }; + bind_to_address = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + example = "any"; + description = '' + The address for the daemon to listen on. + Use `any` to listen on all addresses. + ''; + }; - port = lib.mkOption { - type = lib.types.port; - default = 6600; - description = '' - This setting is the TCP port that is desired for the daemon to get assigned - to. - ''; - }; + port = lib.mkOption { + type = lib.types.port; + default = 6600; + description = '' + This setting is the TCP port that is desired for the daemon to get assigned + to. + ''; + }; - db_file = lib.mkOption { - type = lib.types.path; - default = "${cfg.dataDir}/tag_cache"; - defaultText = lib.literalExpression ''"''${dataDir}/tag_cache"''; - description = '' - The path to MPD's database. - ''; - }; + db_file = lib.mkOption { + type = lib.types.path; + default = "${cfg.dataDir}/tag_cache"; + defaultText = lib.literalExpression ''"''${dataDir}/tag_cache"''; + description = '' + The path to MPD's database. + ''; + }; }; }; default = { };