nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1{
2 config,
3 lib,
4 options,
5 pkgs,
6 ...
7}:
8let
9 cfg = config.services.mpdscribble;
10 mpdCfg = config.services.mpd;
11
12 endpointUrls = {
13 "last.fm" = "http://post.audioscrobbler.com";
14 "libre.fm" = "http://turtle.libre.fm";
15 "jamendo" = "http://postaudioscrobbler.jamendo.com";
16 "listenbrainz" = "http://proxy.listenbrainz.org";
17 };
18
19 mkSection = secname: secCfg: ''
20 [${secname}]
21 url = ${secCfg.url}
22 username = ${secCfg.username}
23 password = {{${secname}_PASSWORD}}
24 journal = /var/lib/mpdscribble/${secname}.journal
25 '';
26
27 endpoints = lib.concatStringsSep "\n" (lib.mapAttrsToList mkSection cfg.endpoints);
28 cfgTemplate = pkgs.writeText "mpdscribble.conf" ''
29 ## This file was automatically genenrated by NixOS and will be overwritten.
30 ## Do not edit. Edit your NixOS configuration instead.
31
32 ## mpdscribble - an audioscrobbler for the Music Player Daemon.
33 ## http://mpd.wikia.com/wiki/Client:mpdscribble
34
35 # HTTP proxy URL.
36 ${lib.optionalString (cfg.proxy != null) "proxy = ${cfg.proxy}"}
37
38 # The location of the mpdscribble log file. The special value
39 # "syslog" makes mpdscribble use the local syslog daemon. On most
40 # systems, log messages will appear in /var/log/daemon.log then.
41 # "-" means log to stderr (the current terminal).
42 log = -
43
44 # How verbose mpdscribble's logging should be. Default is 1.
45 verbose = ${toString cfg.verbose}
46
47 # How often should mpdscribble save the journal file? [seconds]
48 journal_interval = ${toString cfg.journalInterval}
49
50 # The host running MPD, possibly protected by a password
51 # ([PASSWORD@]HOSTNAME).
52 host = ${(lib.optionalString (cfg.passwordFile != null) "{{MPD_PASSWORD}}@") + cfg.host}
53
54 # The port that the MPD listens on and mpdscribble should try to
55 # connect to.
56 port = ${toString cfg.port}
57
58 ${endpoints}
59 '';
60
61 cfgFile = "/run/mpdscribble/mpdscribble.conf";
62
63 replaceSecret =
64 secretFile: placeholder: targetFile:
65 lib.optionalString (
66 secretFile != null
67 ) "${pkgs.replace-secret}/bin/replace-secret '${placeholder}' '${secretFile}' '${targetFile}' ";
68
69 preStart = pkgs.writeShellScript "mpdscribble-pre-start" ''
70 cp -f "${cfgTemplate}" "${cfgFile}"
71 ${replaceSecret cfg.passwordFile "{{MPD_PASSWORD}}" cfgFile}
72 ${lib.concatStringsSep "\n" (
73 lib.mapAttrsToList (
74 secname: cfg: replaceSecret cfg.passwordFile "{{${secname}_PASSWORD}}" cfgFile
75 ) cfg.endpoints
76 )}
77 '';
78
79 localMpd = (cfg.host == "localhost" || cfg.host == "127.0.0.1");
80
81in
82{
83 ###### interface
84
85 options.services.mpdscribble = {
86
87 enable = lib.mkEnableOption "mpdscribble, an MPD client which submits info about tracks being played to Last.fm (formerly AudioScrobbler)";
88
89 proxy = lib.mkOption {
90 default = null;
91 type = lib.types.nullOr lib.types.str;
92 description = ''
93 HTTP proxy URL.
94 '';
95 };
96
97 verbose = lib.mkOption {
98 default = 1;
99 type = lib.types.int;
100 description = ''
101 Log level for the mpdscribble daemon.
102 '';
103 };
104
105 journalInterval = lib.mkOption {
106 default = 600;
107 example = 60;
108 type = lib.types.int;
109 description = ''
110 How often should mpdscribble save the journal file? [seconds]
111 '';
112 };
113
114 host = lib.mkOption {
115 default = (
116 if mpdCfg.settings.bind_to_address != "any" then mpdCfg.settings.bind_to_address else "localhost"
117 );
118 defaultText = lib.literalExpression ''
119 if config.services.mpd.settings.bind_to_address != "any"
120 then config.services.mpd.settings.bind_to_address
121 else "localhost"
122 '';
123 type = lib.types.str;
124 description = ''
125 Host for the mpdscribble daemon to search for a mpd daemon on.
126 '';
127 };
128
129 passwordFile = lib.mkOption {
130 default =
131 if localMpd then
132 (lib.findFirst (c: lib.elem "read" c.permissions) {
133 passwordFile = null;
134 } mpdCfg.credentials).passwordFile
135 else
136 null;
137 defaultText = lib.literalMD ''
138 The first password file with read access configured for MPD when using a local instance,
139 otherwise `null`.
140 '';
141 type = lib.types.nullOr lib.types.str;
142 description = ''
143 File containing the password for the mpd daemon.
144 If there is a local mpd configured using {option}`services.mpd.credentials`
145 the default is automatically set to a matching passwordFile of the local mpd.
146 '';
147 };
148
149 port = lib.mkOption {
150 default = mpdCfg.settings.port;
151 defaultText = lib.literalExpression "config.services.mpd.settings.port";
152 type = lib.types.port;
153 description = ''
154 Port for the mpdscribble daemon to search for a mpd daemon on.
155 '';
156 };
157
158 endpoints = lib.mkOption {
159 type = (
160 let
161 endpoint =
162 { name, ... }:
163 {
164 options = {
165 url = lib.mkOption {
166 type = lib.types.str;
167 default = endpointUrls.${name} or "";
168 description = "The url endpoint where the scrobble API is listening.";
169 };
170 username = lib.mkOption {
171 type = lib.types.str;
172 description = ''
173 Username for the scrobble service.
174 '';
175 };
176 passwordFile = lib.mkOption {
177 type = lib.types.nullOr lib.types.str;
178 description = "File containing the password, either as MD5SUM or cleartext.";
179 };
180 };
181 };
182 in
183 lib.types.attrsOf (lib.types.submodule endpoint)
184 );
185 default = { };
186 example = {
187 "last.fm" = {
188 username = "foo";
189 passwordFile = "/run/secrets/lastfm_password";
190 };
191 };
192 description = ''
193 Endpoints to scrobble to.
194 If the endpoint is one of "${lib.concatStringsSep "\", \"" (lib.attrNames endpointUrls)}" the url is set automatically.
195 '';
196 };
197
198 };
199
200 ###### implementation
201
202 config = lib.mkIf cfg.enable {
203 systemd.services.mpdscribble = {
204 after = [ "network.target" ] ++ (lib.optional localMpd "mpd.service");
205 description = "mpdscribble mpd scrobble client";
206 wantedBy = [ "multi-user.target" ];
207 serviceConfig = {
208 DynamicUser = true;
209 StateDirectory = "mpdscribble";
210 RuntimeDirectory = "mpdscribble";
211 RuntimeDirectoryMode = "700";
212 # TODO use LoadCredential= instead of running preStart with full privileges?
213 ExecStartPre = "+${preStart}";
214 ExecStart = "${pkgs.mpdscribble}/bin/mpdscribble --no-daemon --conf ${cfgFile}";
215 };
216 };
217 };
218
219}