1{ config, pkgs, lib, ... }:
2
3with lib;
4
5let
6 cfg = config.services.plex;
7 plex = pkgs.plex;
8in
9{
10 options = {
11 services.plex = {
12 enable = mkEnableOption "Plex Media Server";
13
14 # FIXME: In order for this config option to work, symlinks in the Plex
15 # package in the Nix store have to be changed to point to this directory.
16 dataDir = mkOption {
17 type = types.str;
18 default = "/var/lib/plex";
19 description = "The directory where Plex stores its data files.";
20 };
21
22 openFirewall = mkOption {
23 type = types.bool;
24 default = false;
25 description = ''
26 Open ports in the firewall for the media server
27 '';
28 };
29
30 user = mkOption {
31 type = types.str;
32 default = "plex";
33 description = "User account under which Plex runs.";
34 };
35
36 group = mkOption {
37 type = types.str;
38 default = "plex";
39 description = "Group under which Plex runs.";
40 };
41
42
43 managePlugins = mkOption {
44 type = types.bool;
45 default = true;
46 description = ''
47 If set to true, this option will cause all of the symlinks in Plex's
48 plugin directory to be removed and symlinks for paths specified in
49 <option>extraPlugins</option> to be added.
50 '';
51 };
52
53 extraPlugins = mkOption {
54 type = types.listOf types.path;
55 default = [];
56 description = ''
57 A list of paths to extra plugin bundles to install in Plex's plugin
58 directory. Every time the systemd unit for Plex starts up, all of the
59 symlinks in Plex's plugin directory will be cleared and this module
60 will symlink all of the paths specified here to that directory. If
61 this behavior is undesired, set <option>managePlugins</option> to
62 false.
63 '';
64 };
65
66 package = mkOption {
67 type = types.package;
68 default = pkgs.plex;
69 defaultText = "pkgs.plex";
70 description = ''
71 The Plex package to use. Plex subscribers may wish to use their own
72 package here, pointing to subscriber-only server versions.
73 '';
74 };
75 };
76 };
77
78 config = mkIf cfg.enable {
79 # Most of this is just copied from the RPM package's systemd service file.
80 systemd.services.plex = {
81 description = "Plex Media Server";
82 after = [ "network.target" ];
83 wantedBy = [ "multi-user.target" ];
84 preStart = ''
85 test -d "${cfg.dataDir}/Plex Media Server" || {
86 echo "Creating initial Plex data directory in \"${cfg.dataDir}\"."
87 mkdir -p "${cfg.dataDir}/Plex Media Server"
88 chown -R ${cfg.user}:${cfg.group} "${cfg.dataDir}"
89 }
90
91 # Copy the database skeleton files to /var/lib/plex/.skeleton
92 # See the the Nix expression for Plex's package for more information on
93 # why this is done.
94 install --owner ${cfg.user} --group ${cfg.group} -d "${cfg.dataDir}/.skeleton"
95 for db in "com.plexapp.plugins.library.db"; do
96 if [ ! -e "${cfg.dataDir}/.skeleton/$db" ]; then
97 cp "${cfg.package}/usr/lib/plexmediaserver/Resources/base_$db" "${cfg.dataDir}/.skeleton/$db"
98 fi
99 chmod u+w "${cfg.dataDir}/.skeleton/$db"
100 chown ${cfg.user}:${cfg.group} "${cfg.dataDir}/.skeleton/$db"
101 done
102
103 # If managePlugins is enabled, setup symlinks for plugins.
104 ${optionalString cfg.managePlugins ''
105 echo "Preparing plugin directory."
106 PLUGINDIR="${cfg.dataDir}/Plex Media Server/Plug-ins"
107 test -d "$PLUGINDIR" || {
108 mkdir -p "$PLUGINDIR";
109 chown ${cfg.user}:${cfg.group} "$PLUGINDIR";
110 }
111
112 echo "Removing old symlinks."
113 # First, remove all of the symlinks in the directory.
114 for f in `ls "$PLUGINDIR/"`; do
115 if [[ -L "$PLUGINDIR/$f" ]]; then
116 echo "Removing plugin symlink $PLUGINDIR/$f."
117 rm "$PLUGINDIR/$f"
118 fi
119 done
120
121 echo "Symlinking plugins."
122 for path in ${toString cfg.extraPlugins}; do
123 dest="$PLUGINDIR/$(basename $path)"
124 if [[ ! -d "$path" ]]; then
125 echo "Error symlinking plugin from $path: no such directory."
126 elif [[ -d "$dest" || -L "$dest" ]]; then
127 echo "Error symlinking plugin from $path to $dest: file or directory already exists."
128 else
129 echo "Symlinking plugin at $path..."
130 ln -s "$path" "$dest"
131 fi
132 done
133 ''}
134 '';
135 serviceConfig = {
136 Type = "simple";
137 User = cfg.user;
138 Group = cfg.group;
139 PermissionsStartOnly = "true";
140 ExecStart = "/bin/sh -c ${cfg.package}/usr/lib/plexmediaserver/Plex\\ Media\\ Server";
141 KillSignal = "SIGQUIT";
142 Restart = "on-failure";
143 };
144 environment = {
145 PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR=cfg.dataDir;
146 PLEX_MEDIA_SERVER_HOME="${cfg.package}/usr/lib/plexmediaserver";
147 PLEX_MEDIA_SERVER_MAX_PLUGIN_PROCS="6";
148 PLEX_MEDIA_SERVER_TMPDIR="/tmp";
149 LD_LIBRARY_PATH="${cfg.package}/usr/lib/plexmediaserver";
150 LC_ALL="en_US.UTF-8";
151 LANG="en_US.UTF-8";
152 };
153 };
154
155 networking.firewall = mkIf cfg.openFirewall {
156 allowedTCPPorts = [ 32400 3005 8324 32469 ];
157 allowedUDPPorts = [ 1900 5353 32410 32412 32413 32414 ];
158 };
159
160 users.extraUsers = mkIf (cfg.user == "plex") {
161 plex = {
162 group = cfg.group;
163 uid = config.ids.uids.plex;
164 };
165 };
166
167 users.extraGroups = mkIf (cfg.group == "plex") {
168 plex = {
169 gid = config.ids.gids.plex;
170 };
171 };
172 };
173}