diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix index 8f8e8b6be0368..1da9e8b644e92 100644 --- a/nixos/modules/services/audio/mpd.nix +++ b/nixos/modules/services/audio/mpd.nix @@ -6,6 +6,17 @@ }: let + inherit (lib) + isList + isBool + isAttrs + concatStringsSep + optionalString + foldlAttrs + boolToString + literalExpression + ; + name = "mpd"; uid = config.ids.uids.mpd; @@ -17,41 +28,83 @@ let let placeholders = ( lib.imap0 ( - i: c: ''password "{{password-${toString i}}}@${lib.concatStringsSep "," c.permissions}"'' + i: c: ''password "{{password-${toString i}}}@${concatStringsSep "," c.permissions}"'' ) creds ); in - lib.concatStringsSep "\n" placeholders + concatStringsSep "\n" placeholders ); - mpdConf = pkgs.writeText "mpd.conf" '' - # 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.musicDirectory}" - playlist_directory "${cfg.playlistDirectory}" - ${lib.optionalString (cfg.dbFile != null) '' - db_file "${cfg.dbFile}" - ''} - 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}"''} - ${lib.optionalString (cfg.fluidsynth) '' - decoder { - plugin "fluidsynth" - soundfont "${pkgs.soundfont-fluid}/share/soundfonts/FluidR3_GM2-2.sf2" - } - ''} - - ${lib.optionalString (cfg.credentials != [ ]) (credentialsPlaceholder cfg.credentials)} - - ${cfg.extraConfig} - ''; + mpdConf = + let + mkSetting = + with builtins; + k: v: + if isAttrs v then + '' + ${k} { + ${foldlAttrs ( + acc: name: value: + "${acc} ${mkSetting name value}" + ) "" v} + } + '' + else if isList v then + foldl' (acc: elem: acc + mkSetting k elem) "" v + else if isBool v then + '' + ${k} "${boolToString v}" + '' + else + '' + ${k} "${toString v}" + ''; + in + pkgs.writeText "mpd.conf" '' + # 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.musicDirectory}" + playlist_directory "${cfg.playlistDirectory}" + ${optionalString (cfg.dbFile != null) '' + db_file "${cfg.dbFile}" + ''} + state_file "${cfg.dataDir}/state" + sticker_file "${cfg.dataDir}/sticker.sql" + + ${optionalString ( + cfg.network.listenAddress != "any" + ) ''bind_to_address "${cfg.network.listenAddress}"''} + ${optionalString (cfg.network.port != 6600) ''port "${toString cfg.network.port}"''} + ${optionalString (cfg.fluidsynth) '' + decoder { + plugin "fluidsynth" + soundfont "${pkgs.soundfont-fluid}/share/soundfonts/FluidR3_GM2-2.sf2" + } + ''} + + ${optionalString (cfg.credentials != [ ]) (credentialsPlaceholder cfg.credentials)} + + ${optionalString (!isNull cfg.settings) ( + foldlAttrs ( + acc: name: value: + acc + mkSetting name value + ) "" cfg.settings + )} + + ${cfg.extraConfig} + ''; + + baseType = + with lib.types; + oneOf [ + str + bool + int + float + path + ]; in { @@ -195,7 +248,7 @@ in default = [ "read" ]; description = '' List of permissions that are granted with this password. - Permissions can be "${lib.concatStringsSep "\", \"" perms}". + Permissions can be "${lib.concatStringsSep ''", "'' perms}". ''; }; }; @@ -229,6 +282,42 @@ in If set, add fluidsynth soundfont and configure the plugin. ''; }; + + settings = lib.mkOption { + type = + with lib.types; + nullOr ( + attrsOf (oneOf [ + baseType + (listOf baseType) + (attrsOf baseType) + (listOf (attrsOf baseType)) + ]) + ); + default = null; + description = '' + Manages the configuration file declaratively. + ''; + example = literalExpression '' + { + # Configure two outputs; Both Pipewire and PulseAudio, the latter being disabled by default + audio_output = [ + { name = "Pipewire"; type = "pipewire"; } + { name = "PulseAudio"; type = "pulse"; enabled = false; } + ]; + + # Configure one input. Can also be multiple inputs by using a list of attribute sets like audio_output above + input = { plugin = "curl"; proxy = "proxy.local"; }; + + # Configuring a database plugin + database = { plugin = "simple"; path = /var/lib/mpd/db; }; + + # Set a replay gain + replaygain = "album"; + replaygain_preamp = 5; + } + ''; + }; }; }; @@ -245,7 +334,7 @@ in listenStreams = [ "" # Note: this is needed to override the upstream unit ( - if pkgs.lib.hasPrefix "/" cfg.network.listenAddress then + if lib.hasPrefix "/" cfg.network.listenAddress then cfg.network.listenAddress else "${ @@ -267,7 +356,7 @@ in lib.concatStringsSep "\n" ( lib.imap0 ( i: c: - ''${pkgs.replace-secret}/bin/replace-secret '{{password-${toString i}}}' '${c.passwordFile}' /run/mpd/mpd.conf'' + "${pkgs.replace-secret}/bin/replace-secret '{{password-${toString i}}}' '${c.passwordFile}' /run/mpd/mpd.conf" ) cfg.credentials ) ); @@ -304,9 +393,7 @@ in }; }; - users.groups = lib.optionalAttrs (cfg.group == name) { - ${name}.gid = gid; - }; + users.groups = lib.optionalAttrs (cfg.group == name) { ${name}.gid = gid; }; }; }