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}