2021-12-05 21:03:03 +00:00
|
|
|
{ config, lib, options, pkgs, ... }:
|
2020-09-09 15:19:16 +00:00
|
|
|
|
|
|
|
with lib;
|
|
|
|
|
|
|
|
let
|
|
|
|
cfg = config.services.mpdscribble;
|
|
|
|
mpdCfg = config.services.mpd;
|
2021-12-05 21:03:03 +00:00
|
|
|
mpdOpt = options.services.mpd;
|
2020-09-09 15:19:16 +00:00
|
|
|
|
|
|
|
endpointUrls = {
|
|
|
|
"last.fm" = "http://post.audioscrobbler.com";
|
|
|
|
"libre.fm" = "http://turtle.libre.fm";
|
|
|
|
"jamendo" = "http://postaudioscrobbler.jamendo.com";
|
|
|
|
"listenbrainz" = "http://proxy.listenbrainz.org";
|
|
|
|
};
|
|
|
|
|
|
|
|
mkSection = secname: secCfg: ''
|
|
|
|
[${secname}]
|
|
|
|
url = ${secCfg.url}
|
|
|
|
username = ${secCfg.username}
|
|
|
|
password = {{${secname}_PASSWORD}}
|
|
|
|
journal = /var/lib/mpdscribble/${secname}.journal
|
|
|
|
'';
|
|
|
|
|
|
|
|
endpoints = concatStringsSep "\n" (mapAttrsToList mkSection cfg.endpoints);
|
|
|
|
cfgTemplate = pkgs.writeText "mpdscribble.conf" ''
|
|
|
|
## This file was automatically genenrated by NixOS and will be overwritten.
|
|
|
|
## Do not edit. Edit your NixOS configuration instead.
|
|
|
|
|
|
|
|
## mpdscribble - an audioscrobbler for the Music Player Daemon.
|
|
|
|
## http://mpd.wikia.com/wiki/Client:mpdscribble
|
|
|
|
|
|
|
|
# HTTP proxy URL.
|
|
|
|
${optionalString (cfg.proxy != null) "proxy = ${cfg.proxy}"}
|
|
|
|
|
|
|
|
# The location of the mpdscribble log file. The special value
|
|
|
|
# "syslog" makes mpdscribble use the local syslog daemon. On most
|
|
|
|
# systems, log messages will appear in /var/log/daemon.log then.
|
|
|
|
# "-" means log to stderr (the current terminal).
|
|
|
|
log = -
|
|
|
|
|
|
|
|
# How verbose mpdscribble's logging should be. Default is 1.
|
|
|
|
verbose = ${toString cfg.verbose}
|
|
|
|
|
|
|
|
# How often should mpdscribble save the journal file? [seconds]
|
|
|
|
journal_interval = ${toString cfg.journalInterval}
|
|
|
|
|
|
|
|
# The host running MPD, possibly protected by a password
|
|
|
|
# ([PASSWORD@]HOSTNAME).
|
|
|
|
host = ${(optionalString (cfg.passwordFile != null) "{{MPD_PASSWORD}}@") + cfg.host}
|
|
|
|
|
|
|
|
# The port that the MPD listens on and mpdscribble should try to
|
|
|
|
# connect to.
|
|
|
|
port = ${toString cfg.port}
|
|
|
|
|
|
|
|
${endpoints}
|
|
|
|
'';
|
|
|
|
|
|
|
|
cfgFile = "/run/mpdscribble/mpdscribble.conf";
|
|
|
|
|
|
|
|
replaceSecret = secretFile: placeholder: targetFile:
|
|
|
|
optionalString (secretFile != null) ''
|
2021-05-04 14:10:57 +00:00
|
|
|
${pkgs.replace-secret}/bin/replace-secret '${placeholder}' '${secretFile}' '${targetFile}' '';
|
2020-09-09 15:19:16 +00:00
|
|
|
|
|
|
|
preStart = pkgs.writeShellScript "mpdscribble-pre-start" ''
|
|
|
|
cp -f "${cfgTemplate}" "${cfgFile}"
|
|
|
|
${replaceSecret cfg.passwordFile "{{MPD_PASSWORD}}" cfgFile}
|
|
|
|
${concatStringsSep "\n" (mapAttrsToList (secname: cfg:
|
|
|
|
replaceSecret cfg.passwordFile "{{${secname}_PASSWORD}}" cfgFile)
|
|
|
|
cfg.endpoints)}
|
|
|
|
'';
|
|
|
|
|
|
|
|
localMpd = (cfg.host == "localhost" || cfg.host == "127.0.0.1");
|
|
|
|
|
|
|
|
in {
|
|
|
|
###### interface
|
|
|
|
|
|
|
|
options.services.mpdscribble = {
|
|
|
|
|
2024-04-13 12:54:15 +00:00
|
|
|
enable = mkEnableOption "mpdscribble, an MPD client which submits info about tracks being played to Last.fm (formerly AudioScrobbler)";
|
2020-09-09 15:19:16 +00:00
|
|
|
|
|
|
|
proxy = mkOption {
|
|
|
|
default = null;
|
|
|
|
type = types.nullOr types.str;
|
2024-04-13 12:54:15 +00:00
|
|
|
description = ''
|
2020-09-09 15:19:16 +00:00
|
|
|
HTTP proxy URL.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
verbose = mkOption {
|
|
|
|
default = 1;
|
|
|
|
type = types.int;
|
2024-04-13 12:54:15 +00:00
|
|
|
description = ''
|
2020-09-09 15:19:16 +00:00
|
|
|
Log level for the mpdscribble daemon.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
journalInterval = mkOption {
|
|
|
|
default = 600;
|
|
|
|
example = 60;
|
|
|
|
type = types.int;
|
2024-04-13 12:54:15 +00:00
|
|
|
description = ''
|
2020-09-09 15:19:16 +00:00
|
|
|
How often should mpdscribble save the journal file? [seconds]
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
host = mkOption {
|
|
|
|
default = (if mpdCfg.network.listenAddress != "any" then
|
|
|
|
mpdCfg.network.listenAddress
|
|
|
|
else
|
|
|
|
"localhost");
|
2021-12-05 21:03:03 +00:00
|
|
|
defaultText = literalExpression ''
|
|
|
|
if config.${mpdOpt.network.listenAddress} != "any"
|
|
|
|
then config.${mpdOpt.network.listenAddress}
|
|
|
|
else "localhost"
|
|
|
|
'';
|
2020-09-09 15:19:16 +00:00
|
|
|
type = types.str;
|
2024-04-13 12:54:15 +00:00
|
|
|
description = ''
|
2020-09-09 15:19:16 +00:00
|
|
|
Host for the mpdscribble daemon to search for a mpd daemon on.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
passwordFile = mkOption {
|
|
|
|
default = if localMpd then
|
|
|
|
(findFirst
|
|
|
|
(c: any (x: x == "read") c.permissions)
|
|
|
|
{ passwordFile = null; }
|
|
|
|
mpdCfg.credentials).passwordFile
|
|
|
|
else
|
|
|
|
null;
|
2022-08-20 20:27:20 +00:00
|
|
|
defaultText = literalMD ''
|
2021-12-05 21:06:49 +00:00
|
|
|
The first password file with read access configured for MPD when using a local instance,
|
2022-08-20 20:27:20 +00:00
|
|
|
otherwise `null`.
|
2021-12-05 21:06:49 +00:00
|
|
|
'';
|
2020-09-09 15:19:16 +00:00
|
|
|
type = types.nullOr types.str;
|
2024-04-13 12:54:15 +00:00
|
|
|
description = ''
|
2020-09-09 15:19:16 +00:00
|
|
|
File containing the password for the mpd daemon.
|
2022-07-28 21:19:15 +00:00
|
|
|
If there is a local mpd configured using {option}`services.mpd.credentials`
|
2020-09-09 15:19:16 +00:00
|
|
|
the default is automatically set to a matching passwordFile of the local mpd.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
port = mkOption {
|
|
|
|
default = mpdCfg.network.port;
|
2021-12-05 21:03:03 +00:00
|
|
|
defaultText = literalExpression "config.${mpdOpt.network.port}";
|
2020-09-09 15:19:16 +00:00
|
|
|
type = types.port;
|
2024-04-13 12:54:15 +00:00
|
|
|
description = ''
|
2020-09-09 15:19:16 +00:00
|
|
|
Port for the mpdscribble daemon to search for a mpd daemon on.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
endpoints = mkOption {
|
|
|
|
type = (let
|
|
|
|
endpoint = { name, ... }: {
|
|
|
|
options = {
|
|
|
|
url = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = endpointUrls.${name} or "";
|
2024-04-13 12:54:15 +00:00
|
|
|
description = "The url endpoint where the scrobble API is listening.";
|
2020-09-09 15:19:16 +00:00
|
|
|
};
|
|
|
|
username = mkOption {
|
|
|
|
type = types.str;
|
2024-04-13 12:54:15 +00:00
|
|
|
description = ''
|
2020-09-09 15:19:16 +00:00
|
|
|
Username for the scrobble service.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
passwordFile = mkOption {
|
|
|
|
type = types.nullOr types.str;
|
2024-04-13 12:54:15 +00:00
|
|
|
description = "File containing the password, either as MD5SUM or cleartext.";
|
2020-09-09 15:19:16 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
in types.attrsOf (types.submodule endpoint));
|
|
|
|
default = { };
|
|
|
|
example = {
|
|
|
|
"last.fm" = {
|
|
|
|
username = "foo";
|
|
|
|
passwordFile = "/run/secrets/lastfm_password";
|
|
|
|
};
|
|
|
|
};
|
2024-04-13 12:54:15 +00:00
|
|
|
description = ''
|
2020-09-09 15:19:16 +00:00
|
|
|
Endpoints to scrobble to.
|
|
|
|
If the endpoint is one of "${
|
|
|
|
concatStringsSep "\", \"" (attrNames endpointUrls)
|
|
|
|
}" the url is set automatically.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
###### implementation
|
|
|
|
|
|
|
|
config = mkIf cfg.enable {
|
|
|
|
systemd.services.mpdscribble = {
|
|
|
|
after = [ "network.target" ] ++ (optional localMpd "mpd.service");
|
|
|
|
description = "mpdscribble mpd scrobble client";
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
serviceConfig = {
|
|
|
|
DynamicUser = true;
|
|
|
|
StateDirectory = "mpdscribble";
|
|
|
|
RuntimeDirectory = "mpdscribble";
|
|
|
|
RuntimeDirectoryMode = "700";
|
|
|
|
# TODO use LoadCredential= instead of running preStart with full privileges?
|
|
|
|
ExecStartPre = "+${preStart}";
|
|
|
|
ExecStart =
|
|
|
|
"${pkgs.mpdscribble}/bin/mpdscribble --no-daemon --conf ${cfgFile}";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|