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