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