grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / misc / airsonic.nix
blob3231fce35dabe550a9afe40f6544f0b9bddd1e29
1 { config, lib, options, pkgs, ... }:
2 let
3   cfg = config.services.airsonic;
4   opt = options.services.airsonic;
5 in {
6   options = {
8     services.airsonic = {
9       enable = lib.mkEnableOption "Airsonic, the Free and Open Source media streaming server (fork of Subsonic and Libresonic)";
11       user = lib.mkOption {
12         type = lib.types.str;
13         default = "airsonic";
14         description = "User account under which airsonic runs.";
15       };
17       home = lib.mkOption {
18         type = lib.types.path;
19         default = "/var/lib/airsonic";
20         description = ''
21           The directory where Airsonic will create files.
22           Make sure it is writable.
23         '';
24       };
26       virtualHost = lib.mkOption {
27         type = lib.types.nullOr lib.types.str;
28         default = null;
29         description = ''
30           Name of the nginx virtualhost to use and setup. If null, do not setup any virtualhost.
31         '';
32       };
34       listenAddress = lib.mkOption {
35         type = lib.types.str;
36         default = "127.0.0.1";
37         description = ''
38           The host name or IP address on which to bind Airsonic.
39           The default value is appropriate for first launch, when the
40           default credentials are easy to guess. It is also appropriate
41           if you intend to use the virtualhost option in the service
42           module. In other cases, you may want to change this to a
43           specific IP or 0.0.0.0 to listen on all interfaces.
44         '';
45       };
47       port = lib.mkOption {
48         type = lib.types.port;
49         default = 4040;
50         description = ''
51           The port on which Airsonic will listen for
52           incoming HTTP traffic. Set to 0 to disable.
53         '';
54       };
56       contextPath = lib.mkOption {
57         type = lib.types.path;
58         default = "/";
59         description = ''
60           The context path, i.e., the last part of the Airsonic
61           URL. Typically '/' or '/airsonic'. Default '/'
62         '';
63       };
65       maxMemory = lib.mkOption {
66         type = lib.types.int;
67         default = 100;
68         description = ''
69           The memory limit (max Java heap size) in megabytes.
70           Default: 100
71         '';
72       };
74       transcoders = lib.mkOption {
75         type = lib.types.listOf lib.types.path;
76         default = [ "${pkgs.ffmpeg.bin}/bin/ffmpeg" ];
77         defaultText = lib.literalExpression ''[ "''${pkgs.ffmpeg.bin}/bin/ffmpeg" ]'';
78         description = ''
79           List of paths to transcoder executables that should be accessible
80           from Airsonic. Symlinks will be created to each executable inside
81           ''${config.${opt.home}}/transcoders.
82         '';
83       };
85       jre = lib.mkPackageOption pkgs "jre8" {
86         extraDescription = ''
87           ::: {.note}
88           Airsonic only supports Java 8, airsonic-advanced requires at least
89           Java 11.
90           :::
91         '';
92       };
94       war = lib.mkOption {
95         type = lib.types.path;
96         default = "${pkgs.airsonic}/webapps/airsonic.war";
97         defaultText = lib.literalExpression ''"''${pkgs.airsonic}/webapps/airsonic.war"'';
98         description = "Airsonic war file to use.";
99       };
101       jvmOptions = lib.mkOption {
102         description = ''
103           Extra command line options for the JVM running AirSonic.
104           Useful for sending jukebox output to non-default alsa
105           devices.
106         '';
107         default = [
108         ];
109         type = lib.types.listOf lib.types.str;
110         example = [
111           "-Djavax.sound.sampled.Clip='#CODEC [plughw:1,0]'"
112           "-Djavax.sound.sampled.Port='#Port CODEC [hw:1]'"
113           "-Djavax.sound.sampled.SourceDataLine='#CODEC [plughw:1,0]'"
114           "-Djavax.sound.sampled.TargetDataLine='#CODEC [plughw:1,0]'"
115         ];
116       };
118     };
119   };
121   config = lib.mkIf cfg.enable {
122     systemd.services.airsonic = {
123       description = "Airsonic Media Server";
124       after = [ "network.target" ];
125       wantedBy = [ "multi-user.target" ];
127       preStart = ''
128         # Install transcoders.
129         rm -rf ${cfg.home}/transcode
130         mkdir -p ${cfg.home}/transcode
131         for exe in ${toString cfg.transcoders}; do
132           ln -sf "$exe" ${cfg.home}/transcode
133         done
134       '';
135       serviceConfig = {
136         ExecStart = ''
137           ${cfg.jre}/bin/java -Xmx${toString cfg.maxMemory}m \
138           -Dairsonic.home=${cfg.home} \
139           -Dserver.address=${cfg.listenAddress} \
140           -Dserver.port=${toString cfg.port} \
141           -Dserver.context-path=${cfg.contextPath} \
142           -Djava.awt.headless=true \
143           ${lib.optionalString (cfg.virtualHost != null)
144             "-Dserver.use-forward-headers=true"} \
145           ${toString cfg.jvmOptions} \
146           -verbose:gc \
147           -jar ${cfg.war}
148         '';
149         Restart = "always";
150         User = "airsonic";
151         UMask = "0022";
152       };
153     };
155     services.nginx = lib.mkIf (cfg.virtualHost != null) {
156       enable = true;
157       recommendedProxySettings = true;
158       virtualHosts.${cfg.virtualHost} = {
159         locations.${cfg.contextPath}.proxyPass = "http://${cfg.listenAddress}:${toString cfg.port}";
160       };
161     };
163     users.users.airsonic = {
164       description = "Airsonic service user";
165       group = "airsonic";
166       name = cfg.user;
167       home = cfg.home;
168       createHome = true;
169       isSystemUser = true;
170     };
171     users.groups.airsonic = {};
172   };