Merge pull request #93450 from ardumont/gerbera-service
mediatomb: Improve service + add gerbera support and tests
This commit is contained in:
commit
19ac436cf5
@ -226,7 +226,30 @@ GRANT ALL PRIVILEGES ON *.* TO 'mysql'@'localhost' WITH GRANT OPTION;
|
|||||||
<filename>testing-python.nix</filename> respectively.
|
<filename>testing-python.nix</filename> respectively.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</itemizedlist>
|
<listitem>
|
||||||
|
<para>
|
||||||
|
The <link linked="opt-services.mediatomb">mediatomb service</link>
|
||||||
|
declares new options. It also adapts existing options so the
|
||||||
|
configuration generation is now lazy. The existing option
|
||||||
|
<literal>customCfg</literal> (defaults to false), when enabled, stops
|
||||||
|
the service configuration generation completely. It then expects the
|
||||||
|
users to provide their own correct configuration at the right location
|
||||||
|
(whereas the configuration was generated and not used at all before).
|
||||||
|
The new option <literal>transcodingOption</literal> (defaults to no)
|
||||||
|
allows a generated configuration. It makes the mediatomb service pulls
|
||||||
|
the necessary runtime dependencies in the nix store (whereas it was
|
||||||
|
generated with hardcoded values before). The new option
|
||||||
|
<literal>mediaDirectories</literal> allows the users to declare autoscan
|
||||||
|
media directories from their nixos configuration:
|
||||||
|
<programlisting>
|
||||||
|
services.mediatomb.mediaDirectories = [
|
||||||
|
{ path = "/var/lib/mediatomb/pictures"; recursive = false; hidden-files = false; }
|
||||||
|
{ path = "/var/lib/mediatomb/audio"; recursive = true; hidden-files = false; }
|
||||||
|
];
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section xmlns="http://docbook.org/ns/docbook"
|
<section xmlns="http://docbook.org/ns/docbook"
|
||||||
@ -863,6 +886,23 @@ CREATE ROLE postgres LOGIN SUPERUSER;
|
|||||||
</listitem>
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
</para>
|
</para>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
The <link linked="opt-services.mediatomb">mediatomb service</link> is
|
||||||
|
now using by default the new and maintained fork
|
||||||
|
<literal>gerbera</literal> package instead of the unmaintained
|
||||||
|
<literal>mediatomb</literal> package. If you want to keep the old
|
||||||
|
behavior, you must declare it with:
|
||||||
|
<programlisting>
|
||||||
|
services.mediatomb.package = pkgs.mediatomb;
|
||||||
|
</programlisting>
|
||||||
|
One new option <literal>openFirewall<literal> has been introduced which
|
||||||
|
defaults to false. If you relied on the service declaration to add the
|
||||||
|
firewall rules itself before, you should now declare it with:
|
||||||
|
<programlisting>
|
||||||
|
services.mediatomb.openFirewall = true;
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
</section>
|
</section>
|
||||||
|
@ -6,37 +6,97 @@ let
|
|||||||
|
|
||||||
gid = config.ids.gids.mediatomb;
|
gid = config.ids.gids.mediatomb;
|
||||||
cfg = config.services.mediatomb;
|
cfg = config.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: "<directory location=\"${d.path}\" mode=\"inotify\" recursive=\"${optionYesNo d.recursive}\" hidden-files=\"${optionYesNo d.hidden-files}\" />\n";
|
||||||
|
|
||||||
mtConf = pkgs.writeText "config.xml" ''
|
transcodingConfig = if cfg.transcoding then with pkgs; ''
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<transcoding enabled="yes">
|
||||||
<config version="2" xmlns="http://mediatomb.cc/config/2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://mediatomb.cc/config/2 http://mediatomb.cc/config/2.xsd">
|
<mimetype-profile-mappings>
|
||||||
|
<transcode mimetype="video/x-flv" using="vlcmpeg" />
|
||||||
|
<transcode mimetype="application/ogg" using="vlcmpeg" />
|
||||||
|
<transcode mimetype="audio/ogg" using="ogg2mp3" />
|
||||||
|
<transcode mimetype="audio/x-flac" using="oggflac2raw"/>
|
||||||
|
</mimetype-profile-mappings>
|
||||||
|
<profiles>
|
||||||
|
<profile name="ogg2mp3" enabled="no" type="external">
|
||||||
|
<mimetype>audio/mpeg</mimetype>
|
||||||
|
<accept-url>no</accept-url>
|
||||||
|
<first-resource>yes</first-resource>
|
||||||
|
<accept-ogg-theora>no</accept-ogg-theora>
|
||||||
|
<agent command="${ffmpeg}/bin/ffmpeg" arguments="-y -i %in -f mp3 %out" />
|
||||||
|
<buffer size="1048576" chunk-size="131072" fill-size="262144" />
|
||||||
|
</profile>
|
||||||
|
<profile name="vlcmpeg" enabled="no" type="external">
|
||||||
|
<mimetype>video/mpeg</mimetype>
|
||||||
|
<accept-url>yes</accept-url>
|
||||||
|
<first-resource>yes</first-resource>
|
||||||
|
<accept-ogg-theora>yes</accept-ogg-theora>
|
||||||
|
<agent command="${libsForQt5.vlc}/bin/vlc"
|
||||||
|
arguments="-I dummy %in --sout #transcode{venc=ffmpeg,vcodec=mp2v,vb=4096,fps=25,aenc=ffmpeg,acodec=mpga,ab=192,samplerate=44100,channels=2}:standard{access=file,mux=ps,dst=%out} vlc:quit" />
|
||||||
|
<buffer size="14400000" chunk-size="512000" fill-size="120000" />
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
</transcoding>
|
||||||
|
'' else ''
|
||||||
|
<transcoding enabled="no">
|
||||||
|
</transcoding>
|
||||||
|
'';
|
||||||
|
|
||||||
|
configText = optionalString (! cfg.customCfg) ''
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<config version="2" xmlns="http://mediatomb.cc/config/2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://mediatomb.cc/config/2 http://mediatomb.cc/config/2.xsd">
|
||||||
<server>
|
<server>
|
||||||
<ui enabled="yes" show-tooltips="yes">
|
<ui enabled="yes" show-tooltips="yes">
|
||||||
<accounts enabled="no" session-timeout="30">
|
<accounts enabled="no" session-timeout="30">
|
||||||
<account user="mediatomb" password="mediatomb"/>
|
<account user="${name}" password="${name}"/>
|
||||||
</accounts>
|
</accounts>
|
||||||
</ui>
|
</ui>
|
||||||
<name>${cfg.serverName}</name>
|
<name>${cfg.serverName}</name>
|
||||||
<udn>uuid:${cfg.uuid}</udn>
|
<udn>uuid:${cfg.uuid}</udn>
|
||||||
<home>${cfg.dataDir}</home>
|
<home>${cfg.dataDir}</home>
|
||||||
<webroot>${pkgs.mediatomb}/share/mediatomb/web</webroot>
|
<interface>${cfg.interface}</interface>
|
||||||
|
<webroot>${pkg}/share/${name}/web</webroot>
|
||||||
|
<pc-directory upnp-hide="${optionYesNo cfg.pcDirectoryHide}"/>
|
||||||
<storage>
|
<storage>
|
||||||
<sqlite3 enabled="yes">
|
<sqlite3 enabled="yes">
|
||||||
<database-file>mediatomb.db</database-file>
|
<database-file>${name}.db</database-file>
|
||||||
</sqlite3>
|
</sqlite3>
|
||||||
</storage>
|
</storage>
|
||||||
<protocolInfo extend="${if cfg.ps3Support then "yes" else "no"}"/>
|
<protocolInfo extend="${optionYesNo cfg.ps3Support}"/>
|
||||||
${if cfg.dsmSupport then ''
|
${optionalString cfg.dsmSupport ''
|
||||||
<custom-http-headers>
|
<custom-http-headers>
|
||||||
<add header="X-User-Agent: redsonic"/>
|
<add header="X-User-Agent: redsonic"/>
|
||||||
</custom-http-headers>
|
</custom-http-headers>
|
||||||
|
|
||||||
<manufacturerURL>redsonic.com</manufacturerURL>
|
<manufacturerURL>redsonic.com</manufacturerURL>
|
||||||
<modelNumber>105</modelNumber>
|
<modelNumber>105</modelNumber>
|
||||||
'' else ""}
|
''}
|
||||||
${if cfg.tg100Support then ''
|
${optionalString cfg.tg100Support ''
|
||||||
<upnp-string-limit>101</upnp-string-limit>
|
<upnp-string-limit>101</upnp-string-limit>
|
||||||
'' else ""}
|
''}
|
||||||
<extended-runtime-options>
|
<extended-runtime-options>
|
||||||
<mark-played-items enabled="yes" suppress-cds-updates="yes">
|
<mark-played-items enabled="yes" suppress-cds-updates="yes">
|
||||||
<string mode="prepend">*</string>
|
<string mode="prepend">*</string>
|
||||||
@ -47,11 +107,14 @@ let
|
|||||||
</extended-runtime-options>
|
</extended-runtime-options>
|
||||||
</server>
|
</server>
|
||||||
<import hidden-files="no">
|
<import hidden-files="no">
|
||||||
|
<autoscan use-inotify="auto">
|
||||||
|
${concatMapStrings toMediaDirectory cfg.mediaDirectories}
|
||||||
|
</autoscan>
|
||||||
<scripting script-charset="UTF-8">
|
<scripting script-charset="UTF-8">
|
||||||
<common-script>${pkgs.mediatomb}/share/mediatomb/js/common.js</common-script>
|
<common-script>${pkg}/share/${name}/js/common.js</common-script>
|
||||||
<playlist-script>${pkgs.mediatomb}/share/mediatomb/js/playlists.js</playlist-script>
|
<playlist-script>${pkg}/share/${name}/js/playlists.js</playlist-script>
|
||||||
<virtual-layout type="builtin">
|
<virtual-layout type="builtin">
|
||||||
<import-script>${pkgs.mediatomb}/share/mediatomb/js/import.js</import-script>
|
<import-script>${pkg}/share/${name}/js/import.js</import-script>
|
||||||
</virtual-layout>
|
</virtual-layout>
|
||||||
</scripting>
|
</scripting>
|
||||||
<mappings>
|
<mappings>
|
||||||
@ -75,12 +138,12 @@ let
|
|||||||
<map from="flv" to="video/x-flv"/>
|
<map from="flv" to="video/x-flv"/>
|
||||||
<map from="mkv" to="video/x-matroska"/>
|
<map from="mkv" to="video/x-matroska"/>
|
||||||
<map from="mka" to="audio/x-matroska"/>
|
<map from="mka" to="audio/x-matroska"/>
|
||||||
${if cfg.ps3Support then ''
|
${optionalString cfg.ps3Support ''
|
||||||
<map from="avi" to="video/divx"/>
|
<map from="avi" to="video/divx"/>
|
||||||
'' else ""}
|
''}
|
||||||
${if cfg.dsmSupport then ''
|
${optionalString cfg.dsmSupport ''
|
||||||
<map from="avi" to="video/avi"/>
|
<map from="avi" to="video/avi"/>
|
||||||
'' else ""}
|
''}
|
||||||
</extension-mimetype>
|
</extension-mimetype>
|
||||||
<mimetype-upnpclass>
|
<mimetype-upnpclass>
|
||||||
<map from="audio/*" to="object.item.audioItem.musicTrack"/>
|
<map from="audio/*" to="object.item.audioItem.musicTrack"/>
|
||||||
@ -108,46 +171,27 @@ let
|
|||||||
</mappings>
|
</mappings>
|
||||||
<online-content>
|
<online-content>
|
||||||
<YouTube enabled="no" refresh="28800" update-at-start="no" purge-after="604800" racy-content="exclude" format="mp4" hd="no">
|
<YouTube enabled="no" refresh="28800" update-at-start="no" purge-after="604800" racy-content="exclude" format="mp4" hd="no">
|
||||||
<favorites user="mediatomb"/>
|
<favorites user="${name}"/>
|
||||||
<standardfeed feed="most_viewed" time-range="today"/>
|
<standardfeed feed="most_viewed" time-range="today"/>
|
||||||
<playlists user="mediatomb"/>
|
<playlists user="${name}"/>
|
||||||
<uploads user="mediatomb"/>
|
<uploads user="${name}"/>
|
||||||
<standardfeed feed="recently_featured" time-range="today"/>
|
<standardfeed feed="recently_featured" time-range="today"/>
|
||||||
</YouTube>
|
</YouTube>
|
||||||
</online-content>
|
</online-content>
|
||||||
</import>
|
</import>
|
||||||
<transcoding enabled="${if cfg.transcoding then "yes" else "no"}">
|
${transcodingConfig}
|
||||||
<mimetype-profile-mappings>
|
|
||||||
<transcode mimetype="video/x-flv" using="vlcmpeg"/>
|
|
||||||
<transcode mimetype="application/ogg" using="vlcmpeg"/>
|
|
||||||
<transcode mimetype="application/ogg" using="oggflac2raw"/>
|
|
||||||
<transcode mimetype="audio/x-flac" using="oggflac2raw"/>
|
|
||||||
</mimetype-profile-mappings>
|
|
||||||
<profiles>
|
|
||||||
<profile name="oggflac2raw" enabled="no" type="external">
|
|
||||||
<mimetype>audio/L16</mimetype>
|
|
||||||
<accept-url>no</accept-url>
|
|
||||||
<first-resource>yes</first-resource>
|
|
||||||
<accept-ogg-theora>no</accept-ogg-theora>
|
|
||||||
<agent command="ogg123" arguments="-d raw -o byteorder:big -f %out %in"/>
|
|
||||||
<buffer size="1048576" chunk-size="131072" fill-size="262144"/>
|
|
||||||
</profile>
|
|
||||||
<profile name="vlcmpeg" enabled="no" type="external">
|
|
||||||
<mimetype>video/mpeg</mimetype>
|
|
||||||
<accept-url>yes</accept-url>
|
|
||||||
<first-resource>yes</first-resource>
|
|
||||||
<accept-ogg-theora>yes</accept-ogg-theora>
|
|
||||||
<agent command="vlc" arguments="-I dummy %in --sout #transcode{venc=ffmpeg,vcodec=mp2v,vb=4096,fps=25,aenc=ffmpeg,acodec=mpga,ab=192,samplerate=44100,channels=2}:standard{access=file,mux=ps,dst=%out} vlc:quit"/>
|
|
||||||
<buffer size="14400000" chunk-size="512000" fill-size="120000"/>
|
|
||||||
</profile>
|
|
||||||
</profiles>
|
|
||||||
</transcoding>
|
|
||||||
</config>
|
</config>
|
||||||
'';
|
'';
|
||||||
|
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 {
|
in {
|
||||||
|
|
||||||
|
|
||||||
###### interface
|
###### interface
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
@ -158,18 +202,27 @@ in {
|
|||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
description = ''
|
description = ''
|
||||||
Whether to enable the mediatomb DLNA server.
|
Whether to enable the Gerbera/Mediatomb DLNA server.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
serverName = mkOption {
|
serverName = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = "mediatomb";
|
default = "Gerbera (Mediatomb)";
|
||||||
description = ''
|
description = ''
|
||||||
How to identify the server on the network.
|
How to identify the server on the network.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
package = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
example = literalExample "pkgs.mediatomb";
|
||||||
|
default = pkgs.gerbera;
|
||||||
|
description = ''
|
||||||
|
Underlying package to be used with the module (default: pkgs.gerbera).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
ps3Support = mkOption {
|
ps3Support = mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
@ -206,23 +259,34 @@ in {
|
|||||||
|
|
||||||
dataDir = mkOption {
|
dataDir = mkOption {
|
||||||
type = types.path;
|
type = types.path;
|
||||||
default = "/var/lib/mediatomb";
|
default = "/var/lib/${name}";
|
||||||
description = ''
|
description = ''
|
||||||
The directory where mediatomb stores its state, data, etc.
|
The directory where ${cfg.serverName} 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 {
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
default = "mediatomb";
|
default = "mediatomb";
|
||||||
description = "User account under which mediatomb runs.";
|
description = "User account under which ${name} runs.";
|
||||||
};
|
};
|
||||||
|
|
||||||
group = mkOption {
|
group = mkOption {
|
||||||
|
type = types.str;
|
||||||
default = "mediatomb";
|
default = "mediatomb";
|
||||||
description = "Group account under which mediatomb runs.";
|
description = "Group account under which ${name} runs.";
|
||||||
};
|
};
|
||||||
|
|
||||||
port = mkOption {
|
port = mkOption {
|
||||||
|
type = types.int;
|
||||||
default = 49152;
|
default = 49152;
|
||||||
description = ''
|
description = ''
|
||||||
The network port to listen on.
|
The network port to listen on.
|
||||||
@ -230,40 +294,72 @@ in {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface = mkOption {
|
interface = mkOption {
|
||||||
|
type = types.str;
|
||||||
default = "";
|
default = "";
|
||||||
description = ''
|
description = ''
|
||||||
A specific interface to bind to.
|
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 the 1900 (tcp and udp) and ${toString cfg.port} (tcp) ports.
|
||||||
|
If the option cfg.interface is set, the firewall rules opened are
|
||||||
|
dedicated to that interface. Otherwise, those rules are opened
|
||||||
|
globally.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
uuid = mkOption {
|
uuid = mkOption {
|
||||||
|
type = types.str;
|
||||||
default = "fdfc8a4e-a3ad-4c1d-b43d-a2eedb03a687";
|
default = "fdfc8a4e-a3ad-4c1d-b43d-a2eedb03a687";
|
||||||
description = ''
|
description = ''
|
||||||
A unique (on your network) to identify the server by.
|
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 {
|
customCfg = mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
description = ''
|
description = ''
|
||||||
Allow mediatomb to create and use its own config file inside ${cfg.dataDir}.
|
Allow ${name} to create and use its own config file inside ${cfg.dataDir}.
|
||||||
|
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
|
||||||
|
${cfg.dataDir}/config.xml. It's up to the user to make a correct configuration file.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
###### implementation
|
###### implementation
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
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 = {
|
systemd.services.mediatomb = {
|
||||||
description = "MediaTomb media Server";
|
description = "${cfg.serverName} media Server";
|
||||||
after = [ "network.target" ];
|
after = [ "network.target" ];
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
path = [ pkgs.mediatomb ];
|
serviceConfig.ExecStart = "${binaryCommand} --port ${toString cfg.port} ${interfaceFlag} ${configFlag} --home ${cfg.dataDir}";
|
||||||
serviceConfig.ExecStart = "${pkgs.mediatomb}/bin/mediatomb -p ${toString cfg.port} ${if cfg.interface!="" then "-e ${cfg.interface}" else ""} ${if cfg.customCfg then "" else "-c ${mtConf}"} -m ${cfg.dataDir}";
|
serviceConfig.User = cfg.user;
|
||||||
serviceConfig.User = "${cfg.user}";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
users.groups = optionalAttrs (cfg.group == "mediatomb") {
|
users.groups = optionalAttrs (cfg.group == "mediatomb") {
|
||||||
@ -274,15 +370,18 @@ in {
|
|||||||
mediatomb = {
|
mediatomb = {
|
||||||
isSystemUser = true;
|
isSystemUser = true;
|
||||||
group = cfg.group;
|
group = cfg.group;
|
||||||
home = "${cfg.dataDir}";
|
home = cfg.dataDir;
|
||||||
createHome = true;
|
createHome = true;
|
||||||
description = "Mediatomb DLNA Server User";
|
description = "${name} DLNA Server User";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
networking.firewall = {
|
# Open firewall only if users enable it
|
||||||
allowedUDPPorts = [ 1900 cfg.port ];
|
networking.firewall = mkMerge [
|
||||||
allowedTCPPorts = [ cfg.port ];
|
(mkIf (cfg.openFirewall && cfg.interface != "") {
|
||||||
};
|
interfaces."${cfg.interface}" = defaultFirewallRules;
|
||||||
|
})
|
||||||
|
(mkIf (cfg.openFirewall && cfg.interface == "") defaultFirewallRules)
|
||||||
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
81
nixos/tests/mediatomb.nix
Normal file
81
nixos/tests/mediatomb.nix
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import ./make-test-python.nix ({ pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
name = "mediatomb";
|
||||||
|
|
||||||
|
nodes = {
|
||||||
|
serverGerbera =
|
||||||
|
{ ... }:
|
||||||
|
let port = 49152;
|
||||||
|
in {
|
||||||
|
imports = [ ../modules/profiles/minimal.nix ];
|
||||||
|
services.mediatomb = {
|
||||||
|
enable = true;
|
||||||
|
serverName = "Gerbera";
|
||||||
|
package = pkgs.gerbera;
|
||||||
|
interface = "eth1"; # accessible from test
|
||||||
|
openFirewall = true;
|
||||||
|
mediaDirectories = [
|
||||||
|
{ path = "/var/lib/gerbera/pictures"; recursive = false; hidden-files = false; }
|
||||||
|
{ path = "/var/lib/gerbera/audio"; recursive = true; hidden-files = false; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
serverMediatomb =
|
||||||
|
{ ... }:
|
||||||
|
let port = 49151;
|
||||||
|
in {
|
||||||
|
imports = [ ../modules/profiles/minimal.nix ];
|
||||||
|
services.mediatomb = {
|
||||||
|
enable = true;
|
||||||
|
serverName = "Mediatomb";
|
||||||
|
package = pkgs.mediatomb;
|
||||||
|
interface = "eth1";
|
||||||
|
inherit port;
|
||||||
|
mediaDirectories = [
|
||||||
|
{ path = "/var/lib/mediatomb/pictures"; recursive = false; hidden-files = false; }
|
||||||
|
{ path = "/var/lib/mediatomb/audio"; recursive = true; hidden-files = false; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
networking.firewall.interfaces.eth1 = {
|
||||||
|
allowedUDPPorts = [ 1900 port ];
|
||||||
|
allowedTCPPorts = [ port ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
client = { ... }: { };
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript =
|
||||||
|
''
|
||||||
|
start_all()
|
||||||
|
|
||||||
|
port = 49151
|
||||||
|
serverMediatomb.succeed("mkdir -p /var/lib/mediatomb/{pictures,audio}")
|
||||||
|
serverMediatomb.succeed("chown -R mediatomb:mediatomb /var/lib/mediatomb")
|
||||||
|
serverMediatomb.wait_for_unit("mediatomb")
|
||||||
|
serverMediatomb.wait_for_open_port(port)
|
||||||
|
serverMediatomb.succeed(f"curl --fail http://serverMediatomb:{port}/")
|
||||||
|
page = client.succeed(f"curl --fail http://serverMediatomb:{port}/")
|
||||||
|
assert "MediaTomb" in page and "Gerbera" not in page
|
||||||
|
serverMediatomb.shutdown()
|
||||||
|
|
||||||
|
port = 49152
|
||||||
|
serverGerbera.succeed("mkdir -p /var/lib/mediatomb/{pictures,audio}")
|
||||||
|
serverGerbera.succeed("chown -R mediatomb:mediatomb /var/lib/mediatomb")
|
||||||
|
# service running gerbera fails the first time claiming something is already bound
|
||||||
|
# gerbera[715]: 2020-07-18 23:52:14 info: Please check if another instance of Gerbera or
|
||||||
|
# gerbera[715]: 2020-07-18 23:52:14 info: another application is running on port TCP 49152 or UDP 1900.
|
||||||
|
# I did not find anything so here I work around this
|
||||||
|
serverGerbera.succeed("sleep 2")
|
||||||
|
serverGerbera.wait_until_succeeds("systemctl restart mediatomb")
|
||||||
|
serverGerbera.wait_for_unit("mediatomb")
|
||||||
|
serverGerbera.succeed(f"curl --fail http://serverGerbera:{port}/")
|
||||||
|
page = client.succeed(f"curl --fail http://serverGerbera:{port}/")
|
||||||
|
assert "Gerbera" in page and "MediaTomb" not in page
|
||||||
|
|
||||||
|
serverGerbera.shutdown()
|
||||||
|
client.shutdown()
|
||||||
|
'';
|
||||||
|
})
|
Loading…
x
Reference in New Issue
Block a user