1 { config, lib, options, pkgs, ... }:
6 cfg = config.services.mpdscribble;
7 mpdCfg = config.services.mpd;
8 mpdOpt = options.services.mpd;
11 "last.fm" = "http://post.audioscrobbler.com";
12 "libre.fm" = "http://turtle.libre.fm";
13 "jamendo" = "http://postaudioscrobbler.jamendo.com";
14 "listenbrainz" = "http://proxy.listenbrainz.org";
17 mkSection = secname: secCfg: ''
20 username = ${secCfg.username}
21 password = {{${secname}_PASSWORD}}
22 journal = /var/lib/mpdscribble/${secname}.journal
25 endpoints = concatStringsSep "\n" (mapAttrsToList mkSection cfg.endpoints);
26 cfgTemplate = pkgs.writeText "mpdscribble.conf" ''
27 ## This file was automatically genenrated by NixOS and will be overwritten.
28 ## Do not edit. Edit your NixOS configuration instead.
30 ## mpdscribble - an audioscrobbler for the Music Player Daemon.
31 ## http://mpd.wikia.com/wiki/Client:mpdscribble
34 ${optionalString (cfg.proxy != null) "proxy = ${cfg.proxy}"}
36 # The location of the mpdscribble log file. The special value
37 # "syslog" makes mpdscribble use the local syslog daemon. On most
38 # systems, log messages will appear in /var/log/daemon.log then.
39 # "-" means log to stderr (the current terminal).
42 # How verbose mpdscribble's logging should be. Default is 1.
43 verbose = ${toString cfg.verbose}
45 # How often should mpdscribble save the journal file? [seconds]
46 journal_interval = ${toString cfg.journalInterval}
48 # The host running MPD, possibly protected by a password
49 # ([PASSWORD@]HOSTNAME).
50 host = ${(optionalString (cfg.passwordFile != null) "{{MPD_PASSWORD}}@") + cfg.host}
52 # The port that the MPD listens on and mpdscribble should try to
54 port = ${toString cfg.port}
59 cfgFile = "/run/mpdscribble/mpdscribble.conf";
61 replaceSecret = secretFile: placeholder: targetFile:
62 optionalString (secretFile != null) ''
63 ${pkgs.replace-secret}/bin/replace-secret '${placeholder}' '${secretFile}' '${targetFile}' '';
65 preStart = pkgs.writeShellScript "mpdscribble-pre-start" ''
66 cp -f "${cfgTemplate}" "${cfgFile}"
67 ${replaceSecret cfg.passwordFile "{{MPD_PASSWORD}}" cfgFile}
68 ${concatStringsSep "\n" (mapAttrsToList (secname: cfg:
69 replaceSecret cfg.passwordFile "{{${secname}_PASSWORD}}" cfgFile)
73 localMpd = (cfg.host == "localhost" || cfg.host == "127.0.0.1");
78 options.services.mpdscribble = {
80 enable = mkEnableOption (lib.mdDoc "mpdscribble");
84 type = types.nullOr types.str;
85 description = lib.mdDoc ''
93 description = lib.mdDoc ''
94 Log level for the mpdscribble daemon.
98 journalInterval = mkOption {
102 description = lib.mdDoc ''
103 How often should mpdscribble save the journal file? [seconds]
108 default = (if mpdCfg.network.listenAddress != "any" then
109 mpdCfg.network.listenAddress
112 defaultText = literalExpression ''
113 if config.${mpdOpt.network.listenAddress} != "any"
114 then config.${mpdOpt.network.listenAddress}
118 description = lib.mdDoc ''
119 Host for the mpdscribble daemon to search for a mpd daemon on.
123 passwordFile = mkOption {
124 default = if localMpd then
126 (c: any (x: x == "read") c.permissions)
127 { passwordFile = null; }
128 mpdCfg.credentials).passwordFile
131 defaultText = literalMD ''
132 The first password file with read access configured for MPD when using a local instance,
135 type = types.nullOr types.str;
136 description = lib.mdDoc ''
137 File containing the password for the mpd daemon.
138 If there is a local mpd configured using {option}`services.mpd.credentials`
139 the default is automatically set to a matching passwordFile of the local mpd.
144 default = mpdCfg.network.port;
145 defaultText = literalExpression "config.${mpdOpt.network.port}";
147 description = lib.mdDoc ''
148 Port for the mpdscribble daemon to search for a mpd daemon on.
152 endpoints = mkOption {
154 endpoint = { name, ... }: {
158 default = endpointUrls.${name} or "";
160 lib.mdDoc "The url endpoint where the scrobble API is listening.";
162 username = mkOption {
164 description = lib.mdDoc ''
165 Username for the scrobble service.
168 passwordFile = mkOption {
169 type = types.nullOr types.str;
171 lib.mdDoc "File containing the password, either as MD5SUM or cleartext.";
175 in types.attrsOf (types.submodule endpoint));
180 passwordFile = "/run/secrets/lastfm_password";
183 description = lib.mdDoc ''
184 Endpoints to scrobble to.
185 If the endpoint is one of "${
186 concatStringsSep "\", \"" (attrNames endpointUrls)
187 }" the url is set automatically.
193 ###### implementation
195 config = mkIf cfg.enable {
196 systemd.services.mpdscribble = {
197 after = [ "network.target" ] ++ (optional localMpd "mpd.service");
198 description = "mpdscribble mpd scrobble client";
199 wantedBy = [ "multi-user.target" ];
202 StateDirectory = "mpdscribble";
203 RuntimeDirectory = "mpdscribble";
204 RuntimeDirectoryMode = "700";
205 # TODO use LoadCredential= instead of running preStart with full privileges?
206 ExecStartPre = "+${preStart}";
208 "${pkgs.mpdscribble}/bin/mpdscribble --no-daemon --conf ${cfgFile}";