1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 gid = config.ids.gids.mediatomb;
8 cfg = config.services.mediatomb;
9
10 mtConf = pkgs.writeText "config.xml" ''
11 <?xml version="1.0" encoding="UTF-8"?>
12 <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">
13 <server>
14 <ui enabled="yes" show-tooltips="yes">
15 <accounts enabled="no" session-timeout="30">
16 <account user="mediatomb" password="mediatomb"/>
17 </accounts>
18 </ui>
19 <name>${cfg.serverName}</name>
20 <udn>uuid:${cfg.uuid}</udn>
21 <home>${cfg.dataDir}</home>
22 <webroot>${pkgs.mediatomb}/share/mediatomb/web</webroot>
23 <storage>
24 <sqlite3 enabled="yes">
25 <database-file>mediatomb.db</database-file>
26 </sqlite3>
27 </storage>
28 <protocolInfo extend="${if cfg.ps3Support then "yes" else "no"}"/>
29 ${if cfg.dsmSupport then ''
30 <custom-http-headers>
31 <add header="X-User-Agent: redsonic"/>
32 </custom-http-headers>
33
34 <manufacturerURL>redsonic.com</manufacturerURL>
35 <modelNumber>105</modelNumber>
36 '' else ""}
37 ${if cfg.tg100Support then ''
38 <upnp-string-limit>101</upnp-string-limit>
39 '' else ""}
40 <extended-runtime-options>
41 <mark-played-items enabled="yes" suppress-cds-updates="yes">
42 <string mode="prepend">*</string>
43 <mark>
44 <content>video</content>
45 </mark>
46 </mark-played-items>
47 </extended-runtime-options>
48 </server>
49 <import hidden-files="no">
50 <scripting script-charset="UTF-8">
51 <common-script>${pkgs.mediatomb}/share/mediatomb/js/common.js</common-script>
52 <playlist-script>${pkgs.mediatomb}/share/mediatomb/js/playlists.js</playlist-script>
53 <virtual-layout type="builtin">
54 <import-script>${pkgs.mediatomb}/share/mediatomb/js/import.js</import-script>
55 </virtual-layout>
56 </scripting>
57 <mappings>
58 <extension-mimetype ignore-unknown="no">
59 <map from="mp3" to="audio/mpeg"/>
60 <map from="ogx" to="application/ogg"/>
61 <map from="ogv" to="video/ogg"/>
62 <map from="oga" to="audio/ogg"/>
63 <map from="ogg" to="audio/ogg"/>
64 <map from="ogm" to="video/ogg"/>
65 <map from="asf" to="video/x-ms-asf"/>
66 <map from="asx" to="video/x-ms-asf"/>
67 <map from="wma" to="audio/x-ms-wma"/>
68 <map from="wax" to="audio/x-ms-wax"/>
69 <map from="wmv" to="video/x-ms-wmv"/>
70 <map from="wvx" to="video/x-ms-wvx"/>
71 <map from="wm" to="video/x-ms-wm"/>
72 <map from="wmx" to="video/x-ms-wmx"/>
73 <map from="m3u" to="audio/x-mpegurl"/>
74 <map from="pls" to="audio/x-scpls"/>
75 <map from="flv" to="video/x-flv"/>
76 <map from="mkv" to="video/x-matroska"/>
77 <map from="mka" to="audio/x-matroska"/>
78 ${if cfg.ps3Support then ''
79 <map from="avi" to="video/divx"/>
80 '' else ""}
81 ${if cfg.dsmSupport then ''
82 <map from="avi" to="video/avi"/>
83 '' else ""}
84 </extension-mimetype>
85 <mimetype-upnpclass>
86 <map from="audio/*" to="object.item.audioItem.musicTrack"/>
87 <map from="video/*" to="object.item.videoItem"/>
88 <map from="image/*" to="object.item.imageItem"/>
89 </mimetype-upnpclass>
90 <mimetype-contenttype>
91 <treat mimetype="audio/mpeg" as="mp3"/>
92 <treat mimetype="application/ogg" as="ogg"/>
93 <treat mimetype="audio/ogg" as="ogg"/>
94 <treat mimetype="audio/x-flac" as="flac"/>
95 <treat mimetype="audio/x-ms-wma" as="wma"/>
96 <treat mimetype="audio/x-wavpack" as="wv"/>
97 <treat mimetype="image/jpeg" as="jpg"/>
98 <treat mimetype="audio/x-mpegurl" as="playlist"/>
99 <treat mimetype="audio/x-scpls" as="playlist"/>
100 <treat mimetype="audio/x-wav" as="pcm"/>
101 <treat mimetype="audio/L16" as="pcm"/>
102 <treat mimetype="video/x-msvideo" as="avi"/>
103 <treat mimetype="video/mp4" as="mp4"/>
104 <treat mimetype="audio/mp4" as="mp4"/>
105 <treat mimetype="application/x-iso9660" as="dvd"/>
106 <treat mimetype="application/x-iso9660-image" as="dvd"/>
107 </mimetype-contenttype>
108 </mappings>
109 <online-content>
110 <YouTube enabled="no" refresh="28800" update-at-start="no" purge-after="604800" racy-content="exclude" format="mp4" hd="no">
111 <favorites user="mediatomb"/>
112 <standardfeed feed="most_viewed" time-range="today"/>
113 <playlists user="mediatomb"/>
114 <uploads user="mediatomb"/>
115 <standardfeed feed="recently_featured" time-range="today"/>
116 </YouTube>
117 </online-content>
118 </import>
119 <transcoding enabled="${if cfg.transcoding then "yes" else "no"}">
120 <mimetype-profile-mappings>
121 <transcode mimetype="video/x-flv" using="vlcmpeg"/>
122 <transcode mimetype="application/ogg" using="vlcmpeg"/>
123 <transcode mimetype="application/ogg" using="oggflac2raw"/>
124 <transcode mimetype="audio/x-flac" using="oggflac2raw"/>
125 </mimetype-profile-mappings>
126 <profiles>
127 <profile name="oggflac2raw" enabled="no" type="external">
128 <mimetype>audio/L16</mimetype>
129 <accept-url>no</accept-url>
130 <first-resource>yes</first-resource>
131 <accept-ogg-theora>no</accept-ogg-theora>
132 <agent command="ogg123" arguments="-d raw -o byteorder:big -f %out %in"/>
133 <buffer size="1048576" chunk-size="131072" fill-size="262144"/>
134 </profile>
135 <profile name="vlcmpeg" enabled="no" type="external">
136 <mimetype>video/mpeg</mimetype>
137 <accept-url>yes</accept-url>
138 <first-resource>yes</first-resource>
139 <accept-ogg-theora>yes</accept-ogg-theora>
140 <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"/>
141 <buffer size="14400000" chunk-size="512000" fill-size="120000"/>
142 </profile>
143 </profiles>
144 </transcoding>
145 </config>
146 '';
147
148in {
149
150
151 ###### interface
152
153 options = {
154
155 services.mediatomb = {
156
157 enable = mkOption {
158 type = types.bool;
159 default = false;
160 description = ''
161 Whether to enable the mediatomb DLNA server.
162 '';
163 };
164
165 serverName = mkOption {
166 type = types.string;
167 default = "mediatomb";
168 description = ''
169 How to identify the server on the network.
170 '';
171 };
172
173 ps3Support = mkOption {
174 type = types.bool;
175 default = false;
176 description = ''
177 Whether to enable ps3 specific tweaks.
178 WARNING: incompatible with DSM 320 support.
179 '';
180 };
181
182 dsmSupport = mkOption {
183 type = types.bool;
184 default = false;
185 description = ''
186 Whether to enable D-Link DSM 320 specific tweaks.
187 WARNING: incompatible with ps3 support.
188 '';
189 };
190
191 tg100Support = mkOption {
192 type = types.bool;
193 default = false;
194 description = ''
195 Whether to enable Telegent TG100 specific tweaks.
196 '';
197 };
198
199 transcoding = mkOption {
200 type = types.bool;
201 default = false;
202 description = ''
203 Whether to enable transcoding.
204 '';
205 };
206
207 dataDir = mkOption {
208 type = types.path;
209 default = "/var/lib/mediatomb";
210 description = ''
211 The directory where mediatomb stores its state, data, etc.
212 '';
213 };
214
215 user = mkOption {
216 default = "mediatomb";
217 description = "User account under which mediatomb runs.";
218 };
219
220 group = mkOption {
221 default = "mediatomb";
222 description = "Group account under which mediatomb runs.";
223 };
224
225 port = mkOption {
226 default = 49152;
227 description = ''
228 The network port to listen on.
229 '';
230 };
231
232 interface = mkOption {
233 default = "";
234 description = ''
235 A specific interface to bind to.
236 '';
237 };
238
239 uuid = mkOption {
240 default = "fdfc8a4e-a3ad-4c1d-b43d-a2eedb03a687";
241 description = ''
242 A unique (on your network) to identify the server by.
243 '';
244 };
245
246 customCfg = mkOption {
247 type = types.bool;
248 default = false;
249 description = ''
250 Allow mediatomb to create and use its own config file inside ${cfg.dataDir}.
251 '';
252 };
253 };
254 };
255
256
257 ###### implementation
258
259 config = mkIf cfg.enable {
260 systemd.services.mediatomb = {
261 description = "MediaTomb media Server";
262 after = [ "local-fs.target" "network.target" ];
263 wantedBy = [ "multi-user.target" ];
264 path = [ pkgs.mediatomb ];
265 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}";
266 serviceConfig.User = "${cfg.user}";
267 };
268
269 users.groups = optionalAttrs (cfg.group == "mediatomb") (singleton {
270 name = "mediatomb";
271 gid = gid;
272 });
273
274 users.users = optionalAttrs (cfg.user == "mediatomb") (singleton {
275 name = "mediatomb";
276 isSystemUser = true;
277 group = cfg.group;
278 home = "${cfg.dataDir}";
279 createHome = true;
280 description = "Mediatomb DLNA Server User";
281 });
282
283 networking.firewall = {
284 allowedUDPPorts = [ 1900 cfg.port ];
285 allowedTCPPorts = [ cfg.port ];
286 };
287 };
288}