forked from
lass.pds.lassul.us/superconfig
This repository has no description
9.7 kB
316 lines
1{
2 self,
3 config,
4 pkgs,
5 lib,
6 ...
7}:
8
9let
10 name = "radio";
11
12 music_dir = "/var/music";
13
14 # dash 0.5.13 has a regression where `read` builtin discards input when stdin is a socket
15 # This breaks htgen which uses tcpserver. Pin htgen's dash to 0.5.12 until upstream fixes it.
16 # See: https://git.kernel.org/pub/scm/utils/dash/dash.git/commit/?id=1d072e9c3292281a7eee54c41fec117ff22723e5
17 dash-0_5_12 = pkgs.dash.overrideAttrs (_old: rec {
18 version = "0.5.12";
19 src = pkgs.fetchurl {
20 url = "http://gondor.apana.org.au/~herbert/dash/files/dash-${version}.tar.gz";
21 hash = "sha256-akdKxG6LCzKRbExg32lMggWNMpfYs4W3RQgDDKSo8oo=";
22 };
23 });
24
25 htgen-fixed = pkgs.htgen.override {
26 pkgs = pkgs // {
27 dash = dash-0_5_12;
28 };
29 };
30
31 skip_track = pkgs.writers.writeBashBin "skip_track" ''
32 set -eu
33
34 # TODO come up with new rating, without moving files
35 # current_track=$(${pkgs.curl}/bin/curl -fSs http://localhost:8002/current | ${pkgs.jq}/bin/jq -r .filename)
36 # track_infos=$(${print_current}/bin/print_current)
37 # skip_count=$(${pkgs.attr}/bin/getfattr -n user.skip_count --only-values "$current_track" || echo 0)
38 # if [[ "$current_track" =~ .*/the_playlist/music/.* ]] && [ "$skip_count" -le 2 ]; then
39 # skip_count=$((skip_count+1))
40 # ${pkgs.attr}/bin/setfattr -n user.skip_count -v "$skip_count" "$current_track"
41 # echo skipping: "$track_infos" skip_count: "$skip_count"
42 # else
43 # mkdir -p "$music_dir"/the_playlist/.graveyard/
44 # mv "$current_track" "$music_dir"/the_playlist/.graveyard/
45 # echo killing: "$track_infos"
46 # fi
47 ${pkgs.curl}/bin/curl -fSs -X POST http://localhost:8002/skip |
48 ${pkgs.jq}/bin/jq -r '.filename'
49 '';
50
51 good_track = pkgs.writeBashBin "good_track" ''
52 set -eu
53
54 current_track=$(${pkgs.curl}/bin/curl -fSs http://localhost:8002/current | ${pkgs.jq}/bin/jq -r .filename)
55 track_infos=$(${print_current}/bin/print_current)
56 # TODO come up with new rating, without moving files
57 # if [[ "$current_track" =~ .*/the_playlist/music/.* ]]; then
58 # ${pkgs.attr}/bin/setfattr -n user.skip_count -v 0 "$current_track"
59 # else
60 # mv "$current_track" "$music_dir"/the_playlist/music/ || :
61 # fi
62 echo good: "$track_infos"
63 '';
64
65 print_current = pkgs.writeDashBin "print_current" ''
66 file=$(${pkgs.curl}/bin/curl -fSs http://localhost:8002/current |
67 ${pkgs.jq}/bin/jq -r '.filename' |
68 ${pkgs.gnused}/bin/sed 's,^${music_dir},,'
69 )
70 link=$(${pkgs.curl}/bin/curl http://localhost:8002/current |
71 ${pkgs.jq}/bin/jq -r '.filename' |
72 ${pkgs.gnused}/bin/sed 's@.*\(.\{11\}\)\.ogg@https://youtu.be/\1@'
73 )
74 echo "$file": "$link"
75 '';
76
77in
78{
79 imports = [
80 ./news.nix
81 ./weather.nix
82 self.inputs.stockholm.nixosModules.acl
83 # self.inputs.stockholm.nixosModules.reaktor2
84 ];
85
86 users.users = {
87 "${name}" = rec {
88 inherit name;
89 createHome = true;
90 group = name;
91 uid = pkgs.stockholm.lib.genid_uint31 name;
92 description = "radio manager";
93 home = "/home/${name}";
94 useDefaultShell = true;
95 openssh.authorizedKeys.keys = [
96 self.keys.ssh.barnacle.public
97 self.keys.ssh.yubi_pgp.public
98 self.keys.ssh.yubi1.public
99 self.keys.ssh.yubi2.public
100 self.keys.ssh.solo2.public
101 ];
102 packages = [
103 good_track
104 skip_track
105 print_current
106 ];
107 };
108 };
109
110 users.groups = {
111 "radio" = { };
112 };
113
114 systemd.services.radio_watcher = {
115 wantedBy = [ "multi-user.target" ];
116 after = [ "radio.service" ];
117 serviceConfig = {
118 ExecStart = pkgs.writers.writeDash "radio_watcher" ''
119 set -efux
120 while :; do
121 ${pkgs.curl}/bin/curl -Ss http://localhost:8000/radio.ogg -o /dev/null
122 ${pkgs.systemd}/bin/systemctl restart radio
123 sleep 60
124 done
125 '';
126 Restart = "on-failure";
127 };
128 };
129
130 services.liquidsoap.streams.radio = ./radio.liq;
131 systemd.services.radio = {
132 environment = {
133 RADIO_PORT = "8002";
134 HOOK_TRACK_CHANGE = pkgs.writers.writeDash "on_change" ''
135 set -xefu
136 LIMIT=100000 #how many tracks to keep in the history
137 HISTORY_FILE=/var/lib/radio/recent
138
139 listeners=$(${pkgs.curl}/bin/curl -fSs http://localhost:8000/status-json.xsl |
140 ${pkgs.jq}/bin/jq '[.icestats.source[].listeners] | add' || echo 0)
141 echo "$(${pkgs.coreutils}/bin/date -Is)" "$filename" | ${pkgs.coreutils}/bin/tee -a "$HISTORY_FILE"
142 echo "$(${pkgs.coreutils}/bin/tail -$LIMIT "$HISTORY_FILE")" > "$HISTORY_FILE"
143 '';
144 MUSIC = "${music_dir}/the_playlist";
145 ICECAST_HOST = "localhost";
146 };
147 path = [
148 pkgs.yt-dlp
149 pkgs.bubblewrap
150 ];
151 serviceConfig.User = lib.mkForce "radio";
152 };
153
154 nixpkgs.config.packageOverrides = opkgs: {
155 liquidsoap = opkgs.liquidsoap.override {
156 runtimePackages = with opkgs; [
157 bubblewrap
158 curl
159 ffmpeg
160 yt-dlp
161 ];
162 };
163 icecast = opkgs.icecast.overrideAttrs (old: rec {
164 version = "2.5-beta3";
165
166 src = pkgs.fetchurl {
167 url = "http://downloads.xiph.org/releases/icecast/icecast-${version}.tar.gz";
168 sha256 = "sha256-4FDokoA9zBDYj8RAO/kuTHaZ6jZYBLSJZiX/IYFaCW8=";
169 };
170
171 NIX_CFLAGS_COMPILE = "-Wno-error=implicit-function-declaration";
172
173 buildInputs = old.buildInputs ++ [ pkgs.pkg-config ];
174 });
175 };
176 services.icecast = {
177 enable = true;
178 hostname = "radio.lassul.us";
179 admin.password = "hackme";
180 extraConf = ''
181 <authentication>
182 <source-password>hackme</source-password>
183 <admin-user>admin</admin-user>
184 <admin-password>hackme</admin-password>
185 </authentication>
186 <logging>
187 <accesslog>-</accesslog>
188 <errorlog>-</errorlog>
189 <loglevel>3</loglevel>
190 </logging>
191 <mount type="normal">
192 <mount-name>/radio.badge</mount-name>
193 <queue-size>2048000</queue-size>
194 <burst-size>128000</burst-size>
195 </mount>
196 '';
197 };
198
199 networking.firewall.interfaces.retiolum.allowedTCPPorts = [
200 8001
201 8002
202 ];
203
204 krebs.htgen.radio = {
205 package = htgen-fixed;
206 port = 8001;
207 user = {
208 name = "radio";
209 };
210 scriptFile = pkgs.writeDash "radio" ''
211 set -x
212 case "''${Method:-} ''${Request_URI:-}" in
213 "POST /skip")
214 printf 'HTTP/1.1 200 OK\r\n'
215 printf 'Connection: close\r\n'
216 printf '\r\n'
217 msg=$(${skip_track}/bin/skip_track)
218 echo "$msg"
219 exit
220 ;;
221 "POST /good")
222 printf 'HTTP/1.1 200 OK\r\n'
223 printf 'Connection: close\r\n'
224 printf '\r\n'
225 msg=$(${good_track}/bin/good_track)
226 echo "$msg"
227 exit
228 ;;
229 esac
230 '';
231 };
232
233 security.acme.certs."radio.r".server = config.krebs.ssl.acmeURL;
234
235 networking.firewall.allowedTCPPorts = [
236 80
237 8000
238 ];
239 services.nginx = {
240 enable = true;
241 virtualHosts."radio.r" = {
242 enableACME = true;
243 addSSL = true;
244 locations."/".extraConfig = ''
245 # https://github.com/aswild/icecast-notes#core-nginx-config
246 proxy_pass http://localhost:8000;
247 # Disable request size limit, very important for uploading large files
248 client_max_body_size 0;
249
250 # Enable support `Transfer-Encoding: chunked`
251 chunked_transfer_encoding on;
252
253 # Disable request and response buffering, minimize latency to/from Icecast
254 proxy_buffering off;
255 proxy_request_buffering off;
256
257 # Icecast needs HTTP/1.1, not 1.0 or 2
258 proxy_http_version 1.1;
259
260 # Forward all original request headers
261 proxy_pass_request_headers on;
262
263 # Set some standard reverse proxy headers. Icecast server currently ignores these,
264 # but may support them in a future version so that access logs are more useful.
265 proxy_set_header Host $host;
266 proxy_set_header X-Real-IP $remote_addr;
267 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
268 proxy_set_header X-Forwarded-Proto $scheme;
269
270 # get source ip for weather reports
271 proxy_set_header user-agent "$http_user_agent; client-ip=$remote_addr";
272 '';
273 locations."= /recent".extraConfig = ''
274 default_type "text/plain";
275 alias /var/lib/radio/recent;
276 '';
277 locations."= /current".extraConfig = ''
278 proxy_pass http://localhost:8002;
279 '';
280 locations."= /skip".extraConfig = ''
281 proxy_pass http://localhost:8001;
282 '';
283 locations."= /good".extraConfig = ''
284 proxy_pass http://localhost:8001;
285 '';
286 locations."= /radio.sh".alias = pkgs.writeScript "radio.sh" ''
287 #!/bin/sh
288 trap 'exit 0' EXIT
289 while sleep 1; do
290 mpv \
291 --cache-secs=0 --demuxer-readahead-secs=0 --untimed --cache-pause=no \
292 'http://radio.lassul.us/radio.ogg'
293 done
294 '';
295 locations."= /controls".extraConfig = ''
296 default_type "text/html";
297 alias ${./controls.html};
298 '';
299 extraConfig = ''
300 add_header 'Access-Control-Allow-Origin' '*';
301 add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
302 '';
303 };
304 };
305 services.syncthing.settings.folders."/home/lass/tmp/the_playlist" = {
306 path = lib.mkForce "/var/music/the_playlist";
307 devices = [
308 "mors"
309 "prism"
310 "radio"
311 ];
312 };
313 krebs.acl."/var/music/the_playlist"."u:lass:X".parents = true;
314 krebs.acl."/var/music/the_playlist"."u:lass:rwX" = { };
315 krebs.acl."/var/music/the_playlist"."u:radio:rwX" = { };
316}