This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

superconfig / 2configs / services / flix / default.nix
13 kB 468 lines
1{ config, pkgs, ... }: 2{ 3 users.users.download = { 4 isSystemUser = true; 5 uid = 1001; 6 group = "download"; 7 }; 8 users.groups.download.members = [ 9 "transmission" 10 "sabnzbd" 11 ]; 12 services.transmission = { 13 enable = true; 14 package = pkgs.transmission_4; 15 home = "/var/state/transmission"; 16 group = "download"; 17 downloadDirPermissions = "775"; 18 settings = { 19 download-dir = "/var/download/transmission"; 20 incomplete-dir-enabled = false; 21 rpc-bind-address = "::"; 22 message-level = 1; 23 umask = 18; 24 rpc-whitelist-enabled = false; 25 rpc-host-whitelist-enabled = false; 26 # stop seeding after reaching 5.0 ratio 27 ratio-limit = 5; 28 ratio-limit-enabled = true; 29 # limit concurrent seeding to reduce resource usage 30 seed-queue-enabled = true; 31 seed-queue-size = 20; 32 }; 33 }; 34 35 # garbage collection for old torrents 36 systemd.services.transmission-gc = { 37 description = "Remove old completed torrents from Transmission"; 38 after = [ "transmission.service" ]; 39 path = [ 40 pkgs.transmission_4 41 pkgs.jq 42 pkgs.coreutils 43 pkgs.gawk 44 pkgs.gnugrep 45 pkgs.findutils 46 ]; 47 script = '' 48 set -efu 49 TRANSMISSION_HOST="128.0.0.1:9091" 50 MAX_AGE_DAYS=30 51 MIN_RATIO=5.0 52 53 # get list of all torrents as JSON 54 torrents=$(transmission-remote "$TRANSMISSION_HOST" -l 2>/dev/null | tail -n +2 | head -n -1 || true) 55 56 if [ -z "$torrents" ]; then 57 echo "No torrents found" 58 exit 0 59 fi 60 61 # process each torrent 62 echo "$torrents" | while read -r line; do 63 id=$(echo "$line" | awk '{print $1}' | tr -d '*') 64 # skip if not a valid ID 65 [ -z "$id" ] || [ "$id" = "ID" ] && continue 66 67 # get torrent info 68 info=$(transmission-remote "$TRANSMISSION_HOST" -t "$id" -i 2>/dev/null || continue) 69 70 # extract ratio and completion status 71 ratio=$(echo "$info" | grep "Ratio:" | awk '{print $2}') 72 percent=$(echo "$info" | grep "Percent Done:" | awk '{print $3}' | tr -d '%') 73 state=$(echo "$info" | grep "State:" | cut -d: -f2- | xargs) 74 75 # skip if not 100% complete 76 [ "$percent" != "100" ] && continue 77 78 # check if ratio reached 79 ratio_reached=false 80 if [ -n "$ratio" ] && [ "$ratio" != "None" ]; then 81 if awk "BEGIN {exit !($ratio >= $MIN_RATIO)}"; then 82 ratio_reached=true 83 fi 84 fi 85 86 # check age via date added 87 date_added=$(echo "$info" | grep "Date added:" | cut -d: -f2- | xargs) 88 if [ -n "$date_added" ]; then 89 added_epoch=$(date -d "$date_added" +%s 2>/dev/null || echo 0) 90 now_epoch=$(date +%s) 91 age_days=$(( (now_epoch - added_epoch) / 86400 )) 92 else 93 age_days=0 94 fi 95 96 # remove if ratio reached OR older than max age 97 if [ "$ratio_reached" = "true" ]; then 98 echo "Removing torrent $id (ratio: $ratio >= $MIN_RATIO)" 99 transmission-remote "$TRANSMISSION_HOST" -t "$id" --remove-and-delete 100 elif [ "$age_days" -ge "$MAX_AGE_DAYS" ]; then 101 echo "Removing torrent $id (age: $age_days days >= $MAX_AGE_DAYS)" 102 transmission-remote "$TRANSMISSION_HOST" -t "$id" --remove-and-delete 103 fi 104 done 105 ''; 106 serviceConfig = { 107 Type = "oneshot"; 108 User = "transmission"; 109 }; 110 }; 111 112 systemd.timers.transmission-gc = { 113 wantedBy = [ "timers.target" ]; 114 timerConfig = { 115 OnCalendar = "daily"; 116 Persistent = true; 117 RandomizedDelaySec = "1h"; 118 }; 119 }; 120 systemd.services.transmission-watcher = { 121 wantedBy = [ "multi-user.target" ]; 122 startAt = "*:0/5"; 123 path = [ 124 pkgs.curl 125 pkgs.systemdMinimal 126 ]; 127 script = '' 128 set -efu -o pipefail 129 # don't restart if transmission is currently starting or stopping 130 state=$(systemctl show -p ActiveState --value transmission.service) 131 if [ "$state" = "activating" ] || [ "$state" = "deactivating" ]; then 132 echo "transmission is $state, skipping" 133 exit 0 134 fi 135 # check if transmission responds (with timeout) 136 if ! curl -SsfL --max-time 10 http://transmission.r; then 137 echo "transmission not responding, restarting" 138 systemctl restart transmission.service 139 fi 140 ''; 141 }; 142 143 security.acme.defaults.email = "spam@krebsco.de"; 144 security.acme.acceptTerms = true; 145 security.acme.certs."yellow.r".server = config.krebs.ssl.acmeURL; 146 security.acme.certs."jelly.r".server = config.krebs.ssl.acmeURL; 147 security.acme.certs."radar.r".server = config.krebs.ssl.acmeURL; 148 security.acme.certs."sonar.r".server = config.krebs.ssl.acmeURL; 149 security.acme.certs."transmission.r".server = config.krebs.ssl.acmeURL; 150 services.nginx = { 151 enable = true; 152 package = pkgs.nginx.override { 153 modules = with pkgs.nginxModules; [ 154 fancyindex 155 ]; 156 }; 157 virtualHosts."yellow.r" = { 158 serverAliases = [ "flix.r" ]; 159 default = true; 160 enableACME = true; 161 addSSL = true; 162 locations."/" = { 163 root = "/var/download"; 164 extraConfig = '' 165 fancyindex on; 166 fancyindex_footer "/fancy.html"; 167 include ${pkgs.nginx}/conf/mime.types; 168 include ${pkgs.writeText "extrMime" '' 169 types { 170 video/webm mkv; 171 } 172 ''}; 173 create_full_put_path on; 174 ''; 175 }; 176 locations."/chatty" = { 177 proxyPass = "http://localhost:3000"; 178 extraConfig = '' 179 rewrite /chatty/(.*) /$1 break; 180 proxy_set_header Host $host; 181 ''; 182 }; 183 locations."= /fancy.html".extraConfig = '' 184 alias ${pkgs.writeText "nginx_footer" '' 185 <div id="mydiv"> 186 <!-- Include a header DIV with the same name as the draggable DIV, followed by "header" --> 187 <div id="mydivheader">Click here to move</div> 188 <iframe src="/chatty/index.html"></iframe> 189 </div> 190 <style> 191 #mydiv { 192 position: absolute; 193 z-index: 9; 194 background-color: #f1f1f1; 195 border: 1px solid #d3d3d3; 196 text-align: center; 197 } 198 199 #mydivheader { 200 padding: 10px; 201 cursor: move; 202 z-index: 10; 203 background-color: #2196F3; 204 color: #fff; 205 } 206 </style> 207 <script> 208 // Make the DIV element draggable: 209 dragElement(document.getElementById("mydiv")); 210 211 function dragElement(elmnt) { 212 var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; 213 if (document.getElementById(elmnt.id + "header")) { 214 // if present, the header is where you move the DIV from: 215 document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown; 216 } else { 217 // otherwise, move the DIV from anywhere inside the DIV: 218 elmnt.onmousedown = dragMouseDown; 219 } 220 221 function dragMouseDown(e) { 222 e = e || window.event; 223 e.preventDefault(); 224 // get the mouse cursor position at startup: 225 pos3 = e.clientX; 226 pos4 = e.clientY; 227 document.onmouseup = closeDragElement; 228 // call a function whenever the cursor moves: 229 document.onmousemove = elementDrag; 230 } 231 232 function elementDrag(e) { 233 e = e || window.event; 234 e.preventDefault(); 235 // calculate the new cursor position: 236 pos1 = pos3 - e.clientX; 237 pos2 = pos4 - e.clientY; 238 pos3 = e.clientX; 239 pos4 = e.clientY; 240 // set the element's new position: 241 elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; 242 elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; 243 } 244 245 function closeDragElement() { 246 // stop moving when mouse button is released: 247 document.onmouseup = null; 248 document.onmousemove = null; 249 } 250 } 251 </script> 252 ''}; 253 ''; 254 }; 255 virtualHosts."jelly.r" = { 256 enableACME = true; 257 addSSL = true; 258 locations."/".extraConfig = '' 259 proxy_pass http://localhost:8096/; 260 proxy_set_header Accept-Encoding ""; 261 ''; 262 }; 263 virtualHosts."transmission.r" = { 264 enableACME = true; 265 addSSL = true; 266 locations."/" = { 267 proxyWebsockets = true; 268 proxyPass = "http://128.0.0.1:9091"; 269 }; 270 }; 271 virtualHosts."radar.r" = { 272 enableACME = true; 273 addSSL = true; 274 locations."/" = { 275 proxyWebsockets = true; 276 proxyPass = "http://localhost:7878"; 277 }; 278 }; 279 virtualHosts."sonar.r" = { 280 enableACME = true; 281 addSSL = true; 282 locations."/" = { 283 proxyWebsockets = true; 284 proxyPass = "http://localhost:8989"; 285 # sonar.r now has forced authentication 286 extraConfig = '' 287 proxy_set_header Authorization "Basic a3JlYnM6YWlkc2JhbGxz"; 288 ''; 289 }; 290 }; 291 }; 292 293 services.samba = { 294 enable = true; 295 enableNmbd = false; 296 settings.global = { 297 "hosts allow" = "42::/16 10.243.0.0/16 10.244.0.0/16 fdcc:c5da:5295:c853:d499::/80"; 298 "use sendfile" = "true"; 299 "disable netbios" = "true"; 300 "mangled names" = "illegal"; 301 "socket options" = "TCP_NODELAY IPTOS_LOWDELAY SO_RCVBUF=65536 SO_SNDBUF=65536"; 302 "load printers" = "false"; 303 "disable spoolss" = "true"; 304 "printcap name" = "/dev/null"; 305 "map to guest" = "Bad User"; 306 "max log size" = "50"; 307 "dns proxy" = "no"; 308 "security" = "user"; 309 "syslog only" = "yes"; 310 }; 311 shares.public = { 312 comment = "Warez"; 313 path = "/var/download"; 314 public = "yes"; 315 "only guest" = "yes"; 316 "create mask" = "0644"; 317 "directory mask" = "2777"; 318 writable = "no"; 319 printable = "no"; 320 }; 321 }; 322 323 systemd.services.bruellwuerfel = 324 let 325 bruellwuerfelSrc = pkgs.fetchFromGitHub { 326 owner = "krebs"; 327 repo = "bruellwuerfel"; 328 rev = "dc73adf69249fb63a4b024f1f3fbc9e541b27015"; 329 sha256 = "078jp1gbavdp8lnwa09xa5m6bbbd05fi4x5ldkkgin5z04hwlhmd"; 330 }; 331 in 332 { 333 wantedBy = [ "multi-user.target" ]; 334 after = [ "network-online.target" ]; 335 environment = { 336 IRC_CHANNEL = "#flix"; 337 IRC_NICK = "bruelli"; 338 IRC_SERVER = "irc.r"; 339 IRC_HISTORY_FILE = "/tmp/bruelli.history"; 340 }; 341 serviceConfig = { 342 ExecStart = "${pkgs.deno}/bin/deno run -A ${bruellwuerfelSrc}/src/index.ts"; 343 }; 344 }; 345 346 networking.firewall.allowedTCPPorts = [ 347 80 # nginx 348 443 # nginx 349 9091 # transmission web 350 8096 # jellyfin 351 8920 # jellyfin 352 51413 # transmission traffic 353 445 # smbd 354 111 # smbd 355 2049 # smbd 356 4000 # smbd 357 4001 # smbd 358 4002 # smbd 359 ]; 360 networking.firewall.allowedUDPPorts = [ 361 51413 # transmission traffic 362 1900 # jellyfin 363 7359 # jellyfin 364 111 # smbd 365 2049 # smbd 366 4000 # smbd 367 4001 # smbd 368 4002 # smbd 369 ]; 370 krebs.iptables = { 371 enable = true; 372 tables.nat.PREROUTING.rules = [ 373 # transmission rpc port 374 { 375 predicate = "-i retiolum -p tcp --dport 9091"; 376 target = "DNAT --to-destination fdb4:3310:947::2"; 377 v4 = false; 378 } 379 ]; 380 tables.filter.FORWARD.policy = "ACCEPT"; # we need this so we can forward into the the transmission network namespace 381 }; 382 383 systemd.services.flix-index = { 384 wantedBy = [ "multi-user.target" ]; 385 path = [ 386 pkgs.coreutils 387 pkgs.findutils 388 pkgs.inotify-tools 389 ]; 390 startAt = "hourly"; 391 serviceConfig = { 392 Type = "oneshot"; 393 ExecStart = pkgs.writers.writeDash "flix-index" '' 394 set -efu 395 index(){ 396 find . -type f > "$DIR"/index.tmp 397 mv "$DIR"/index.tmp "$DIR"/index 398 } 399 400 DIR=/var/download 401 cd "$DIR" 402 index 403 ''; 404 }; 405 }; 406 407 services.jellyfin = { 408 enable = true; 409 group = "download"; 410 }; 411 412 # request managment 413 services.jellyseerr = { 414 enable = true; 415 openFirewall = true; 416 }; 417 418 # movies 419 services.radarr = { 420 enable = true; 421 openFirewall = true; 422 user = "download"; 423 group = "download"; 424 }; 425 426 # shows 427 services.sonarr = { 428 enable = true; 429 openFirewall = true; 430 user = "download"; 431 group = "download"; 432 }; 433 # sonarr needs unsecure packages 434 nixpkgs.config.permittedInsecurePackages = [ 435 "aspnetcore-runtime-wrapped-6.0.36" 436 "aspnetcore-runtime-6.0.36" 437 "dotnet-sdk-wrapped-6.0.428" 438 "dotnet-sdk-6.0.428" 439 "olm-3.2.16" 440 ]; 441 442 # indexers 443 services.prowlarr = { 444 enable = true; 445 openFirewall = true; 446 }; 447 448 # flaresolverr for bypassing cloudflare on some indexers 449 services.flaresolverr = { 450 enable = true; 451 openFirewall = true; 452 }; 453 454 # subtitles 455 services.bazarr = { 456 enable = true; 457 openFirewall = true; 458 user = "download"; 459 group = "download"; 460 }; 461 462 # usenet download client 463 services.sabnzbd = { 464 enable = true; 465 group = "download"; 466 openFirewall = true; 467 }; 468}