me like nix
0

Configure Feed

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

1{ inputs, ... }: 2 3let 4 jellyfinKodiSyncQueue = inputs.nixpkgs.legacyPackages.x86_64-linux.fetchzip { 5 url = "https://repo.jellyfin.org/releases/plugin/kodi-sync-queue/kodi-sync-queue_15.0.0.0.zip"; 6 stripRoot = false; 7 hash = "sha256-xtlG3UQ/WClt/Hvxe+oId2CeJ+PWMDXBUJXh5+k+mZQ="; 8 }; 9in 10{ 11 flake.modules.nixos.media-server = 12 { pkgs, config, lib, ... }: 13 { 14 services.flaresolverr.enable = true; 15 16 services.immich = { 17 enable = true; 18 mediaLocation = "/mnt/storage2/immich"; 19 host = "0.0.0.0"; 20 openFirewall = true; 21 }; 22 23 # Immich uses PostgreSQL via Unix socket; avoid conflicting with lbdt-postgres on TCP/5432. 24 services.postgresql.settings.listen_addresses = lib.mkForce ""; 25 26 users.users.immich.extraGroups = [ "storage" ]; 27 28 systemd.tmpfiles.rules = [ 29 "d /mnt/storage2/immich 0750 immich immich -" 30 "d /mnt/storage2/photos 2775 root storage -" 31 "d /mnt/storage2/photos/originals 2775 root storage -" 32 "d /mnt/storage2/photos/exports 2775 root storage -" 33 ]; 34 35 # transmission.service bind-mounts these paths before ExecStartPre runs, while 36 # systemd-tmpfiles refuses to create them because /mnt/storage1 is user-owned. 37 # Create them in a small unsandboxed dependency before Transmission starts. 38 systemd.services.transmission-media-dirs = { 39 description = "Create Transmission media directories"; 40 before = [ "transmission.service" ]; 41 requiredBy = [ "transmission.service" ]; 42 serviceConfig = { 43 Type = "oneshot"; 44 RemainAfterExit = true; 45 }; 46 script = '' 47 install -d -o transmission -g media -m 0755 \ 48 /mnt/storage1/nixarr/media/torrents \ 49 /mnt/storage1/nixarr/media/torrents/.incomplete \ 50 /mnt/storage1/nixarr/media/torrents/.watch 51 ''; 52 }; 53 systemd.services.transmission = { 54 requires = [ "transmission-media-dirs.service" ]; 55 after = [ "transmission-media-dirs.service" ]; 56 }; 57 58 # Keep manually-created Sonarr/Radarr SABnzbd download clients in sync with 59 # SABnzbd's generated API key. This avoids storing the SABnzbd API key in Nix 60 # while repairing clients after the settings-backed SABnzbd config rewrite. 61 systemd.services.sync-sabnzbd-download-client-keys = { 62 description = "Sync SABnzbd API key into Arr download clients"; 63 after = [ "sabnzbd.service" "sonarr.service" "radarr.service" ]; 64 wants = [ "sabnzbd.service" "sonarr.service" "radarr.service" ]; 65 wantedBy = [ "multi-user.target" ]; 66 serviceConfig = { 67 Type = "oneshot"; 68 User = "root"; 69 }; 70 path = [ pkgs.python3 ]; 71 script = '' 72 python3 ${pkgs.writeText "sync-sabnzbd-download-client-keys.py" '' 73 import json 74 import urllib.error 75 import urllib.request 76 from pathlib import Path 77 78 def read_key(path): 79 for line in Path(path).read_text().splitlines(): 80 if line.strip().startswith("api_key"): 81 return line.split("=", 1)[1].strip() 82 raise RuntimeError(f"api_key not found in {path}") 83 84 sab_key = read_key("/var/lib/sabnzbd/sabnzbd.ini") 85 86 services = [ 87 ("Sonarr", "http://127.0.0.1:8989", "/data/.state/nixarr/secrets/sonarr.api-key"), 88 ("Radarr", "http://127.0.0.1:7878", "/data/.state/nixarr/secrets/radarr.api-key"), 89 ] 90 91 def request(base, api_key, method, path, data=None): 92 body = None if data is None else json.dumps(data).encode() 93 req = urllib.request.Request( 94 base + path, 95 data=body, 96 method=method, 97 headers={"X-Api-Key": api_key, "Content-Type": "application/json"}, 98 ) 99 with urllib.request.urlopen(req, timeout=10) as resp: 100 raw = resp.read() 101 return None if not raw else json.loads(raw) 102 103 for name, base, key_file in services: 104 try: 105 api_key = Path(key_file).read_text().strip() 106 clients = request(base, api_key, "GET", "/api/v3/downloadclient") 107 changed = 0 108 for client in clients: 109 if (client.get("implementation") or "").lower() != "sabnzbd" and (client.get("name") or "").lower() != "sabnzbd": 110 continue 111 for field in client.get("fields", []): 112 if field.get("name") == "apiKey" and field.get("value") != sab_key: 113 field["value"] = sab_key 114 changed += 1 115 request(base, api_key, "PUT", f"/api/v3/downloadclient/{client['id']}", client) 116 print(f"{name}: updated {changed} SABnzbd apiKey field(s)") 117 except Exception as e: 118 print(f"{name}: failed to sync SABnzbd API key: {e}") 119 raise 120 ''} 121 ''; 122 }; 123 124 # nixarr configures SABnzbd through services.sabnzbd.settings on newer nixpkgs, 125 # but NixOS keeps the legacy configFile default when system.stateVersion < 26.05, 126 # causing those settings to be ignored. Force the new settings-backed path. 127 services.sabnzbd.configFile = lib.mkForce null; 128 129 age.secrets.wireguard.file = ../secrets/wireguard.age; 130 131 nixarr = { 132 enable = true; 133 mediaDir = "/mnt/storage1/nixarr/media"; 134 vpn = { 135 enable = true; 136 wgConf = config.age.secrets.wireguard.path; 137 }; 138 139 jellyfin = { 140 enable = true; 141 openFirewall = true; 142 }; 143 144 transmission = { 145 enable = true; 146 vpn.enable = true; 147 peerPort = 51413; 148 }; 149 sabnzbd = { 150 enable = true; 151 vpn.enable = true; 152 openFirewall = true; 153 }; 154 155 prowlarr.enable = true; 156 radarr.enable = true; 157 sonarr.enable = true; 158 seerr = { 159 enable = true; 160 openFirewall = true; 161 }; 162 163 recyclarr = { 164 enable = true; 165 configuration = { 166 sonarr = { 167 series = { 168 base_url = "http://localhost:8989"; 169 api_key = "!env_var SONARR_API_KEY"; 170 quality_definition = { 171 type = "series"; 172 }; 173 delete_old_custom_formats = true; 174 custom_formats = [ 175 { 176 trash_ids = [ 177 "85c61753df5da1fb2aab6f2a47426b09" 178 "9c11cd3f07101cdba90a2d81cf0e56b4" 179 ]; 180 assign_scores_to = [ 181 { 182 name = "WEB-DL (1080p)"; 183 score = -10000; 184 } 185 ]; 186 } 187 ]; 188 }; 189 }; 190 radarr = { 191 movies = { 192 base_url = "http://localhost:7878"; 193 api_key = "!env_var RADARR_API_KEY"; 194 quality_definition = { 195 type = "movie"; 196 }; 197 delete_old_custom_formats = true; 198 custom_formats = [ 199 { 200 trash_ids = [ 201 "570bc9ebecd92723d2d21500f4be314c" 202 "eca37840c13c6ef2dd0262b141a5482f" 203 ]; 204 assign_scores_to = [ 205 { 206 name = "HD Bluray + WEB"; 207 score = 25; 208 } 209 ]; 210 } 211 ]; 212 }; 213 }; 214 }; 215 }; 216 }; 217 218 # Avoid jellyfin blocking shutdown for 2 minutes 219 systemd.services.jellyfin.serviceConfig.TimeoutStopSec = 10; 220 221 # Install Kodi Sync Queue plugin into Jellyfin 222 systemd.services.jellyfin.serviceConfig.ExecStartPre = 223 let 224 pluginDir = "/data/.state/nixarr/jellyfin/data/plugins/Kodi Sync Queue/15.0.0.0"; 225 in 226 pkgs.writeShellScript "install-jellyfin-plugins" '' 227 mkdir -p "${pluginDir}" 228 cp -f ${jellyfinKodiSyncQueue}/*.dll ${jellyfinKodiSyncQueue}/meta.json "${pluginDir}/" 229 ''; 230 }; 231}