summary refs log tree commit diff
path: root/nixos/modules/services/misc/plex.nix
blob: 1cd8da768f48dbf44a5fcf84098939727151e905 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
{ config, pkgs, lib, ... }:

with lib;

let
  cfg = config.services.plex;
in
{
  imports = [
    (mkRemovedOptionModule [ "services" "plex" "managePlugins" ] "Please omit or define the option: `services.plex.extraPlugins' instead.")
  ];

  options = {
    services.plex = {
      enable = mkEnableOption "Plex Media Server";

      dataDir = mkOption {
        type = types.str;
        default = "/var/lib/plex";
        description = ''
          The directory where Plex stores its data files.
        '';
      };

      openFirewall = mkOption {
        type = types.bool;
        default = false;
        description = ''
          Open ports in the firewall for the media server.
        '';
      };

      user = mkOption {
        type = types.str;
        default = "plex";
        description = ''
          User account under which Plex runs.
        '';
      };

      group = mkOption {
        type = types.str;
        default = "plex";
        description = ''
          Group under which Plex runs.
        '';
      };

      extraPlugins = mkOption {
        type = types.listOf types.path;
        default = [];
        description = ''
          A list of paths to extra plugin bundles to install in Plex's plugin
          directory. Every time the systemd unit for Plex starts up, all of the
          symlinks in Plex's plugin directory will be cleared and this module
          will symlink all of the paths specified here to that directory.
        '';
        example = literalExpression ''
          [
            (builtins.path {
              name = "Audnexus.bundle";
              path = pkgs.fetchFromGitHub {
                owner = "djdembeck";
                repo = "Audnexus.bundle";
                rev = "v0.2.8";
                sha256 = "sha256-IWOSz3vYL7zhdHan468xNc6C/eQ2C2BukQlaJNLXh7E=";
              };
            })
          ]
        '';
      };

      extraScanners = mkOption {
        type = types.listOf types.path;
        default = [];
        description = ''
          A list of paths to extra scanners to install in Plex's scanners
          directory.

          Every time the systemd unit for Plex starts up, all of the symlinks
          in Plex's scanners directory will be cleared and this module will
          symlink all of the paths specified here to that directory.
        '';
        example = literalExpression ''
          [
            (fetchFromGitHub {
              owner = "ZeroQI";
              repo = "Absolute-Series-Scanner";
              rev = "773a39f502a1204b0b0255903cee4ed02c46fde0";
              sha256 = "4l+vpiDdC8L/EeJowUgYyB3JPNTZ1sauN8liFAcK+PY=";
            })
          ]
        '';
      };

      package = mkOption {
        type = types.package;
        default = pkgs.plex;
        defaultText = literalExpression "pkgs.plex";
        description = ''
          The Plex package to use. Plex subscribers may wish to use their own
          package here, pointing to subscriber-only server versions.
        '';
      };
    };
  };

  config = mkIf cfg.enable {
    # Most of this is just copied from the RPM package's systemd service file.
    systemd.services.plex = {
      description = "Plex Media Server";
      after = [ "network.target" ];
      wantedBy = [ "multi-user.target" ];

      serviceConfig = {
        Type = "simple";
        User = cfg.user;
        Group = cfg.group;

        # Run the pre-start script with full permissions (the "!" prefix) so it
        # can create the data directory if necessary.
        ExecStartPre = let
          preStartScript = pkgs.writeScript "plex-run-prestart" ''
            #!${pkgs.bash}/bin/bash

            # Create data directory if it doesn't exist
            if ! test -d "$PLEX_DATADIR"; then
              echo "Creating initial Plex data directory in: $PLEX_DATADIR"
              install -d -m 0755 -o "${cfg.user}" -g "${cfg.group}" "$PLEX_DATADIR"
            fi
         '';
        in
          "!${preStartScript}";

        ExecStart = "${cfg.package}/bin/plexmediaserver";
        KillSignal = "SIGQUIT";
        Restart = "on-failure";
      };

      environment = {
        # Configuration for our FHS userenv script
        PLEX_DATADIR=cfg.dataDir;
        PLEX_PLUGINS=concatMapStringsSep ":" builtins.toString cfg.extraPlugins;
        PLEX_SCANNERS=concatMapStringsSep ":" builtins.toString cfg.extraScanners;

        # The following variables should be set by the FHS userenv script:
        #   PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR
        #   PLEX_MEDIA_SERVER_HOME

        # Allow access to GPU acceleration; the Plex LD_LIBRARY_PATH is added
        # by the FHS userenv script.
        LD_LIBRARY_PATH="/run/opengl-driver/lib";

        PLEX_MEDIA_SERVER_MAX_PLUGIN_PROCS="6";
        PLEX_MEDIA_SERVER_TMPDIR="/tmp";
        PLEX_MEDIA_SERVER_USE_SYSLOG="true";
        LC_ALL="en_US.UTF-8";
        LANG="en_US.UTF-8";
      };
    };

    networking.firewall = mkIf cfg.openFirewall {
      allowedTCPPorts = [ 32400 3005 8324 32469 ];
      allowedUDPPorts = [ 1900 5353 32410 32412 32413 32414 ];
    };

    users.users = mkIf (cfg.user == "plex") {
      plex = {
        group = cfg.group;
        uid = config.ids.uids.plex;
      };
    };

    users.groups = mkIf (cfg.group == "plex") {
      plex = {
        gid = config.ids.gids.plex;
      };
    };
  };
}