{ 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/mpeg no yes no video/mpeg yes yes yes '' else '' ''; configText = optionalString (! cfg.customCfg) '' ${cfg.serverName} uuid:${cfg.uuid} ${cfg.dataDir} ${cfg.interface} ${pkg}/share/${name}/web ${name}.db ${optionalString cfg.dsmSupport '' redsonic.com 105 ''} ${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; example = literalExpression "pkgs.mediatomb"; 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; }; 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) ]; }; }