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