Skip to content

Commit

Permalink
Merge pull request #97553 from Sohalt/nixos/mpdscribble
Browse files Browse the repository at this point in the history
nixos/mpdscribble: init
  • Loading branch information
doronbehar committed Jan 10, 2021
2 parents c06b2b3 + 59bd4c1 commit 054b7c4
Show file tree
Hide file tree
Showing 2 changed files with 203 additions and 0 deletions.
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Expand Up @@ -226,6 +226,7 @@
./services/audio/icecast.nix
./services/audio/liquidsoap.nix
./services/audio/mpd.nix
./services/audio/mpdscribble.nix
./services/audio/mopidy.nix
./services/audio/roon-server.nix
./services/audio/slimserver.nix
Expand Down
202 changes: 202 additions & 0 deletions nixos/modules/services/audio/mpdscribble.nix
@@ -0,0 +1,202 @@
{ config, lib, pkgs, ... }:

with lib;

let
cfg = config.services.mpdscribble;
mpdCfg = config.services.mpd;

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) ''
${pkgs.replace}/bin/replace-literal -ef ${placeholder} "$(cat ${secretFile})" ${targetFile}'';

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 = {

enable = mkEnableOption "mpdscribble";

proxy = mkOption {
default = null;
type = types.nullOr types.str;
description = ''
HTTP proxy URL.
'';
};

verbose = mkOption {
default = 1;
type = types.int;
description = ''
Log level for the mpdscribble daemon.
'';
};

journalInterval = mkOption {
default = 600;
example = 60;
type = types.int;
description = ''
How often should mpdscribble save the journal file? [seconds]
'';
};

host = mkOption {
default = (if mpdCfg.network.listenAddress != "any" then
mpdCfg.network.listenAddress
else
"localhost");
type = types.str;
description = ''
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;
type = types.nullOr types.str;
description = ''
File containing the password for the mpd daemon.
If there is a local mpd configured using <option>services.mpd.credentials</option>
the default is automatically set to a matching passwordFile of the local mpd.
'';
};

port = mkOption {
default = mpdCfg.network.port;
type = types.port;
description = ''
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 "";
description =
"The url endpoint where the scrobble API is listening.";
};
username = mkOption {
type = types.str;
description = ''
Username for the scrobble service.
'';
};
passwordFile = mkOption {
type = types.nullOr types.str;
description =
"File containing the password, either as MD5SUM or cleartext.";
};
};
};
in types.attrsOf (types.submodule endpoint));
default = { };
example = {
"last.fm" = {
username = "foo";
passwordFile = "/run/secrets/lastfm_password";
};
};
description = ''
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}";
};
};
};

}

0 comments on commit 054b7c4

Please sign in to comment.