{ config, lib, pkgs, ... }: let inherit (lib) mkIf mkOption types; cfg = config.services.mpd; in { options = { services.mpd = { enable = mkOption { type = types.bool; default = false; description = '' Whether to enable MPD, the music player daemon. ''; }; package = mkOption { type = types.package; default = pkgs.mpd; defaultText = "pkgs.mpd"; description = '' The MPD package to run. ''; }; musicDirectory = mkOption { type = with types; either path str; defaultText = lib.literalExpression '' ''${home.homeDirectory}/music if state version < 22.11 ''${xdg.userDirs.music} if xdg.userDirs.enable == true undefined otherwise ''; apply = toString; # Prevent copies to Nix store. description = '' The directory where mpd reads music from. If [](#opt-xdg.userDirs.enable) is `true` then the defined XDG music directory is used. Otherwise, you must explicitly specify a value. ''; }; playlistDirectory = mkOption { type = types.path; default = "${cfg.dataDir}/playlists"; defaultText = "\${dataDir}/playlists"; apply = toString; # Prevent copies to Nix store. description = '' The directory where mpd stores playlists. ''; }; extraConfig = mkOption { type = types.lines; default = ""; description = '' Extra directives added to to the end of MPD's configuration file, {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)`. ''; }; extraArgs = mkOption { type = types.listOf types.str; default = [ ]; example = [ "--verbose" ]; description = '' Extra command-line arguments to pass to MPD. ''; }; dataDir = mkOption { type = types.path; default = "${config.xdg.dataHome}/mpd"; defaultText = "$XDG_DATA_HOME/mpd"; apply = toString; # Prevent copies to Nix store. description = '' The directory where MPD stores its state, tag cache, playlists etc. ''; }; network = { startWhenNeeded = mkOption { type = types.bool; default = false; visible = pkgs.stdenv.hostPlatform.isLinux; readOnly = pkgs.stdenv.hostPlatform.isDarwin; description = '' Enable systemd socket activation. This is only supported on Linux. ''; }; listenAddress = mkOption { type = 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 = mkOption { type = types.port; default = 6600; description = '' The TCP port on which the the daemon will listen. ''; }; }; dbFile = mkOption { type = types.nullOr types.str; default = "${cfg.dataDir}/tag_cache"; defaultText = "\${dataDir}/tag_cache"; description = '' The path to MPD's database. If set to `null` the parameter is omitted from the configuration. ''; }; }; }; config = let mpdConf = pkgs.writeText "mpd.conf" ('' music_directory "${cfg.musicDirectory}" playlist_directory "${cfg.playlistDirectory}" '' + lib.optionalString (cfg.dbFile != null) '' db_file "${cfg.dbFile}" '' + lib.optionalString (pkgs.stdenv.hostPlatform.isDarwin) '' log_file "${config.home.homeDirectory}/Library/Logs/mpd/log.txt" '' + '' 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.extraConfig != "") '' ${cfg.extraConfig} ''); in mkIf cfg.enable { home.packages = [ cfg.package ]; services.mpd = lib.mkMerge [ (mkIf (lib.versionAtLeast config.home.stateVersion "22.11" && config.xdg.userDirs.enable) { musicDirectory = lib.mkOptionDefault config.xdg.userDirs.music; }) (mkIf (lib.versionOlder config.home.stateVersion "22.11") { musicDirectory = lib.mkOptionDefault "${config.home.homeDirectory}/music"; }) ]; systemd.user = lib.mkIf pkgs.stdenv.hostPlatform.isLinux { services.mpd = { Unit = lib.mkMerge [ { Description = "Music Player Daemon"; After = [ "network.target" "sound.target" ]; } (mkIf cfg.network.startWhenNeeded { Requires = [ "mpd.socket" ]; After = [ "mpd.socket" ]; }) ]; Install = mkIf (!cfg.network.startWhenNeeded) { WantedBy = [ "default.target" ]; }; Service = { Environment = [ "PATH=${config.home.profileDirectory}/bin" ]; ExecStart = "${cfg.package}/bin/mpd --no-daemon ${mpdConf} ${ lib.escapeShellArgs cfg.extraArgs }"; Type = "notify"; ExecStartPre = '' ${pkgs.bash}/bin/bash -c "${pkgs.coreutils}/bin/mkdir -p '${cfg.dataDir}' '${cfg.playlistDirectory}'"''; }; }; sockets.mpd = mkIf cfg.network.startWhenNeeded { Socket = { ListenStream = let listen = if cfg.network.listenAddress == "any" then toString cfg.network.port else "${cfg.network.listenAddress}:${toString cfg.network.port}"; in [ listen "%t/mpd/socket" ]; Backlog = 5; KeepAlive = true; }; Install = { WantedBy = [ "sockets.target" ]; }; }; }; launchd.agents.mpd = lib.mkIf pkgs.stdenv.hostPlatform.isDarwin { enable = true; config = { ProgramArguments = [ (lib.getExe cfg.package) "--no-daemon" "${mpdConf}" ] ++ cfg.extraArgs; KeepAlive = true; ProcessType = "Interactive"; }; }; }; }