{ config, lib, options, pkgs, ... }:
with lib;
let
gid = config.ids.gids.mediatomb;
cfg = config.services.mediatomb;
opt = options.services.mediatomb;
name = cfg.package.pname;
pkg = cfg.package;
optionYesNo = option: if option then "yes" else "no";
# configuration on media directory
mediaDirectory = {
options = {
path = mkOption {
type = types.str;
description = ''
Absolute directory path to the media directory to index.
'';
};
recursive = mkOption {
type = types.bool;
default = false;
description = "Whether the indexation must take place recursively or not.";
};
hidden-files = mkOption {
type = types.bool;
default = true;
description = "Whether to index the hidden files or not.";
};
};
};
toMediaDirectory = d: "\n";
transcodingConfig = if cfg.transcoding then with pkgs; ''
audio/mpegnoyesnovideo/mpegyesyesyes
'' else ''
'';
configText = optionalString (! cfg.customCfg) ''
${cfg.serverName}uuid:${cfg.uuid}${cfg.dataDir}${cfg.interface}${pkg}/share/${name}/web${name}.db
${optionalString cfg.dsmSupport ''
redsonic.com105
''}
${optionalString cfg.tg100Support ''
101
''}
*video
${concatMapStrings toMediaDirectory cfg.mediaDirectories}
${pkg}/share/${name}/js/common.js${pkg}/share/${name}/js/playlists.js${pkg}/share/${name}/js/import.js
${optionalString cfg.ps3Support ''
''}
${optionalString cfg.dsmSupport ''
''}
${transcodingConfig}
'';
defaultFirewallRules = {
# udp 1900 port needs to be opened for SSDP (not configurable within
# mediatomb/gerbera) cf.
# http://docs.gerbera.io/en/latest/run.html?highlight=udp%20port#network-setup
allowedUDPPorts = [ 1900 cfg.port ];
allowedTCPPorts = [ cfg.port ];
};
in {
###### interface
options = {
services.mediatomb = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable the Gerbera/Mediatomb DLNA server.
'';
};
serverName = mkOption {
type = types.str;
default = "Gerbera (Mediatomb)";
description = ''
How to identify the server on the network.
'';
};
package = mkOption {
type = types.package;
default = pkgs.gerbera;
defaultText = literalExpression "pkgs.gerbera";
description = ''
Underlying package to be used with the module.
'';
};
ps3Support = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable ps3 specific tweaks.
WARNING: incompatible with DSM 320 support.
'';
};
dsmSupport = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable D-Link DSM 320 specific tweaks.
WARNING: incompatible with ps3 support.
'';
};
tg100Support = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable Telegent TG100 specific tweaks.
'';
};
transcoding = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable transcoding.
'';
};
dataDir = mkOption {
type = types.path;
default = "/var/lib/${name}";
defaultText = literalExpression ''"/var/lib/''${config.${opt.package}.pname}"'';
description = ''
The directory where Gerbera/Mediatomb stores its state, data, etc.
'';
};
pcDirectoryHide = mkOption {
type = types.bool;
default = true;
description = ''
Whether to list the top-level directory or not (from upnp client standpoint).
'';
};
user = mkOption {
type = types.str;
default = "mediatomb";
description = "User account under which the service runs.";
};
group = mkOption {
type = types.str;
default = "mediatomb";
description = "Group account under which the service runs.";
};
port = mkOption {
type = types.int;
default = 49152;
description = ''
The network port to listen on.
'';
};
interface = mkOption {
type = types.str;
default = "";
description = ''
A specific interface to bind to.
'';
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = ''
If false (the default), this is up to the user to declare the firewall rules.
If true, this opens port 1900 (tcp and udp) and the port specified by
.
If the option is set,
the firewall rules opened are dedicated to that interface. Otherwise,
those rules are opened globally.
'';
};
uuid = mkOption {
type = types.str;
default = "fdfc8a4e-a3ad-4c1d-b43d-a2eedb03a687";
description = ''
A unique (on your network) to identify the server by.
'';
};
mediaDirectories = mkOption {
type = with types; listOf (submodule mediaDirectory);
default = [];
description = ''
Declare media directories to index.
'';
example = [
{ path = "/data/pictures"; recursive = false; hidden-files = false; }
{ path = "/data/audio"; recursive = true; hidden-files = false; }
];
};
customCfg = mkOption {
type = types.bool;
default = false;
description = ''
Allow the service to create and use its own config file inside the dataDir as
configured by .
Deactivated by default, the service then runs with the configuration generated from this module.
Otherwise, when enabled, no service configuration is generated. Gerbera/Mediatomb then starts using
config.xml within the configured dataDir. It's up to the user to make a correct
configuration file.
'';
};
};
};
###### implementation
config = let binaryCommand = "${pkg}/bin/${name}";
interfaceFlag = optionalString ( cfg.interface != "") "--interface ${cfg.interface}";
configFlag = optionalString (! cfg.customCfg) "--config ${pkgs.writeText "config.xml" configText}";
in mkIf cfg.enable {
systemd.services.mediatomb = {
description = "${cfg.serverName} media Server";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig.ExecStart = "${binaryCommand} --port ${toString cfg.port} ${interfaceFlag} ${configFlag} --home ${cfg.dataDir}";
serviceConfig.User = cfg.user;
serviceConfig.Group = cfg.group;
};
users.groups = optionalAttrs (cfg.group == "mediatomb") {
mediatomb.gid = gid;
};
users.users = optionalAttrs (cfg.user == "mediatomb") {
mediatomb = {
isSystemUser = true;
group = cfg.group;
home = cfg.dataDir;
createHome = true;
description = "${name} DLNA Server User";
};
};
# Open firewall only if users enable it
networking.firewall = mkMerge [
(mkIf (cfg.openFirewall && cfg.interface != "") {
interfaces."${cfg.interface}" = defaultFirewallRules;
})
(mkIf (cfg.openFirewall && cfg.interface == "") defaultFirewallRules)
];
};
}