1{ config, lib, options, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.subsonic;
7 opt = options.services.subsonic;
8in {
9 options = {
10 services.subsonic = {
11 enable = mkEnableOption (lib.mdDoc "Subsonic daemon");
12
13 home = mkOption {
14 type = types.path;
15 default = "/var/lib/subsonic";
16 description = lib.mdDoc ''
17 The directory where Subsonic will create files.
18 Make sure it is writable.
19 '';
20 };
21
22 listenAddress = mkOption {
23 type = types.str;
24 default = "0.0.0.0";
25 description = lib.mdDoc ''
26 The host name or IP address on which to bind Subsonic.
27 Only relevant if you have multiple network interfaces and want
28 to make Subsonic available on only one of them. The default value
29 will bind Subsonic to all available network interfaces.
30 '';
31 };
32
33 port = mkOption {
34 type = types.port;
35 default = 4040;
36 description = lib.mdDoc ''
37 The port on which Subsonic will listen for
38 incoming HTTP traffic. Set to 0 to disable.
39 '';
40 };
41
42 httpsPort = mkOption {
43 type = types.port;
44 default = 0;
45 description = lib.mdDoc ''
46 The port on which Subsonic will listen for
47 incoming HTTPS traffic. Set to 0 to disable.
48 '';
49 };
50
51 contextPath = mkOption {
52 type = types.path;
53 default = "/";
54 description = lib.mdDoc ''
55 The context path, i.e., the last part of the Subsonic
56 URL. Typically '/' or '/subsonic'. Default '/'
57 '';
58 };
59
60 maxMemory = mkOption {
61 type = types.int;
62 default = 100;
63 description = lib.mdDoc ''
64 The memory limit (max Java heap size) in megabytes.
65 Default: 100
66 '';
67 };
68
69 defaultMusicFolder = mkOption {
70 type = types.path;
71 default = "/var/music";
72 description = lib.mdDoc ''
73 Configure Subsonic to use this folder for music. This option
74 only has effect the first time Subsonic is started.
75 '';
76 };
77
78 defaultPodcastFolder = mkOption {
79 type = types.path;
80 default = "/var/music/Podcast";
81 description = lib.mdDoc ''
82 Configure Subsonic to use this folder for Podcasts. This option
83 only has effect the first time Subsonic is started.
84 '';
85 };
86
87 defaultPlaylistFolder = mkOption {
88 type = types.path;
89 default = "/var/playlists";
90 description = lib.mdDoc ''
91 Configure Subsonic to use this folder for playlists. This option
92 only has effect the first time Subsonic is started.
93 '';
94 };
95
96 transcoders = mkOption {
97 type = types.listOf types.path;
98 default = [ "${pkgs.ffmpeg.bin}/bin/ffmpeg" ];
99 defaultText = literalExpression ''[ "''${pkgs.ffmpeg.bin}/bin/ffmpeg" ]'';
100 description = lib.mdDoc ''
101 List of paths to transcoder executables that should be accessible
102 from Subsonic. Symlinks will be created to each executable inside
103 ''${config.${opt.home}}/transcoders.
104 '';
105 };
106 };
107 };
108
109 config = mkIf cfg.enable {
110 systemd.services.subsonic = {
111 description = "Personal media streamer";
112 after = [ "network.target" ];
113 wantedBy = [ "multi-user.target" ];
114 script = ''
115 ${pkgs.jre8}/bin/java -Xmx${toString cfg.maxMemory}m \
116 -Dsubsonic.home=${cfg.home} \
117 -Dsubsonic.host=${cfg.listenAddress} \
118 -Dsubsonic.port=${toString cfg.port} \
119 -Dsubsonic.httpsPort=${toString cfg.httpsPort} \
120 -Dsubsonic.contextPath=${cfg.contextPath} \
121 -Dsubsonic.defaultMusicFolder=${cfg.defaultMusicFolder} \
122 -Dsubsonic.defaultPodcastFolder=${cfg.defaultPodcastFolder} \
123 -Dsubsonic.defaultPlaylistFolder=${cfg.defaultPlaylistFolder} \
124 -Djava.awt.headless=true \
125 -verbose:gc \
126 -jar ${pkgs.subsonic}/subsonic-booter-jar-with-dependencies.jar
127 '';
128
129 preStart = ''
130 # Formerly this module set cfg.home to /var/subsonic. Try to move
131 # /var/subsonic to cfg.home.
132 oldHome="/var/subsonic"
133 if [ "${cfg.home}" != "$oldHome" ] &&
134 ! [ -e "${cfg.home}" ] &&
135 [ -d "$oldHome" ] &&
136 [ $(${pkgs.coreutils}/bin/stat -c %u "$oldHome") -eq \
137 ${toString config.users.users.subsonic.uid} ]; then
138 logger Moving "$oldHome" to "${cfg.home}"
139 ${pkgs.coreutils}/bin/mv -T "$oldHome" "${cfg.home}"
140 fi
141
142 # Install transcoders.
143 ${pkgs.coreutils}/bin/rm -rf ${cfg.home}/transcode ; \
144 ${pkgs.coreutils}/bin/mkdir -p ${cfg.home}/transcode ; \
145 ${pkgs.bash}/bin/bash -c ' \
146 for exe in "$@"; do \
147 ${pkgs.coreutils}/bin/ln -sf "$exe" ${cfg.home}/transcode; \
148 done' IGNORED_FIRST_ARG ${toString cfg.transcoders}
149 '';
150 serviceConfig = {
151 # Needed for Subsonic to find subsonic.war.
152 WorkingDirectory = "${pkgs.subsonic}";
153 Restart = "always";
154 User = "subsonic";
155 UMask = "0022";
156 };
157 };
158
159 users.users.subsonic = {
160 description = "Subsonic daemon user";
161 home = cfg.home;
162 createHome = true;
163 group = "subsonic";
164 uid = config.ids.uids.subsonic;
165 };
166
167 users.groups.subsonic.gid = config.ids.gids.subsonic;
168 };
169}