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