From 7f587a8f1e57e1edec5d962bd6b45c960e812254 Mon Sep 17 00:00:00 2001 From: rasmus-kirk Date: Fri, 1 Mar 2024 22:49:56 +0100 Subject: [PATCH] Fixed prowlarr/cross-seed groups --- flake.lock | 20 +- flake.nix | 11 +- mkDocs.nix | 2 +- nixarr/default.nix | 243 +------------------- nixarr/jellyfin/default.nix | 2 +- nixarr/lidarr/default.nix | 2 +- nixarr/nixarr.nix | 240 +++++++++++++++++++ nixarr/prowlarr/default.nix | 4 +- nixarr/prowlarr/prowlarr-module/default.nix | 4 +- nixarr/radarr/default.nix | 2 +- nixarr/readarr/default.nix | 2 +- nixarr/transmission/cross-seed/default.nix | 54 ++++- nixarr/transmission/default.nix | 49 ++-- pkgs/cross-seed/default.nix | 83 ++----- 14 files changed, 365 insertions(+), 353 deletions(-) create mode 100644 nixarr/nixarr.nix diff --git a/flake.lock b/flake.lock index 59b63c9..aab114d 100644 --- a/flake.lock +++ b/flake.lock @@ -90,22 +90,6 @@ "type": "github" } }, - "nixpkgs_2": { - "locked": { - "lastModified": 1709218635, - "narHash": "sha256-nytX/MkfqeTD4z7bMq4QRXcHxO9B3vRo9tM6fMtPFA8=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "068d4db604958d05d0b46c47f79b507d84dbc069", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-23.11", - "repo": "nixpkgs", - "type": "github" - } - }, "root": { "inputs": { "devshell": "devshell", @@ -153,7 +137,9 @@ }, "vpnconfinement": { "inputs": { - "nixpkgs": "nixpkgs_2" + "nixpkgs": [ + "nixpkgs" + ] }, "locked": { "lastModified": 1709159289, diff --git a/flake.nix b/flake.nix index 6cb5484..87ad540 100644 --- a/flake.nix +++ b/flake.nix @@ -37,7 +37,6 @@ flake-root.flakeModule treefmt-nix.flakeModule devshell.flakeModule - #vpnconfinement.nixosModules.default ]; systems = [ "x86_64-linux" @@ -45,10 +44,13 @@ flake = { nixosModules = rec { - #vpnconfinement = vpnconfinement.nixosModules.default; nixarr = (import ./nixarr vpnconfinement); - #imports = [ vpnconfinement.nixosModules.default ]; - #nixarr.imports = [ vpnconfinement ]; + + # TODO: Tried these, they don't work + #nixarr = (import + # ./nixarr + #) // { imports = [ inputs.vpnconfinement.default.nixosModule ]; }; + #specialArgs = {inherit inputs;}; default = nixarr; }; }; @@ -56,6 +58,7 @@ perSystem = { config, pkgs, + lib, ... }: { treefmt.config = { diff --git a/mkDocs.nix b/mkDocs.nix index 9199eae..4d2d79c 100644 --- a/mkDocs.nix +++ b/mkDocs.nix @@ -10,7 +10,7 @@ { config._module.check = false; } - ./nixarr + ./nixarr/nixarr.nix ]; }; optionsDocNixos = nixosOptionsDoc { diff --git a/nixarr/default.nix b/nixarr/default.nix index 5cc263e..1984df7 100644 --- a/nixarr/default.nix +++ b/nixarr/default.nix @@ -1,241 +1,8 @@ -vpnconfinement: { - config, - lib, - pkgs, - ... -}: -with lib; let - cfg = config.nixarr; -in { +vpnconfinement: +{ ... }: +{ imports = [ - vpnconfinement.nixosModules.default - ./jellyfin - ./ddns - ./radarr - ./lidarr - ./readarr - ./sonarr - ./openssh - ./prowlarr - ./transmission - ../util + vpnconfinement.default.nixosModule + ./nixarr.nix ]; - - options.nixarr = { - enable = mkOption { - type = types.bool; - default = false; - description = '' - Whether or not to enable the nixarr module. Has the following features: - - - **Run services through a VPN:** You can run any service that this module - supports through a VPN, fx `nixarr.transmission.vpn.enable = true;` - - **Automatic Directories, Users and Permissions:** The module automatically - creates directories and users for your media library. It also sets sane - permissions. - - **State Management:** All services support state management and all state - that they manage is located by default in `/data/.state/nixarr/*` - - **Optional Automatic Port Forwarding:** This module has a UPNP support that - lets services request ports from your router automatically, if you enable it. - - It is possible, _but not recommended_, to run the "*Arrs" behind a VPN, - because it can cause rate limiting issues. Generally, you should use - VPN on transmission and maybe jellyfin, depending on your setup. - - The following services are supported: - - - [Jellyfin](#nixarr.jellyfin.enable) - - [Lidarr](#nixarr.lidarr.enable) - - [Prowlarr](#nixarr.prowlarr.enable) - - [Radarr](#nixarr.radarr.enable) - - [Readarr](#nixarr.readarr.enable) - - [Sonarr](#nixarr.sonarr.enable) - - [Transmission](#nixarr.transmission.enable) - - Remember to read the options. - ''; - }; - - mediaDir = mkOption { - type = types.path; - default = "/data/media"; - description = '' - The location of the media directory for the services. - ''; - }; - - stateDir = mkOption { - type = types.path; - default = "/data/.state"; - description = '' - The location of the state directory for the services. - ''; - }; - - vpn = { - enable = mkOption { - type = types.bool; - default = false; - description = '' - **Required options:** [`nixarr.vpn.wgConf`](#nixarr.vpn.wgconf) - - Whether or not to enable VPN support for the services that nixarr - supports. - ''; - }; - - wgConf = mkOption { - type = types.nullOr types.path; - default = null; - description = "The path to the wireguard configuration file."; - }; - - vpnTestService = { - enable = mkEnableOption '' - the vpn test service. Useful for testing DNS leaks or if the VPN - port forwarding works correctly. - ''; - - port = mkOption { - type = with types; nullOr port; - default = null; - description = '' - The port that netcat listens to on the vpn test service. If set to - `null`, then netcat will not be started. - ''; - example = 58403; - }; - }; - - openTcpPorts = mkOption { - type = with types; listOf port; - default = []; - description = '' - What TCP ports to allow traffic from. You might need this if you're - port forwarding on your VPN provider and you're setting up services - not covered in by this module that uses the VPN. - ''; - example = [46382 38473]; - }; - - openUdpPorts = mkOption { - type = with types; listOf port; - default = []; - description = '' - What UDP ports to allow traffic from. You might need this if you're - port forwarding on your VPN provider and you're setting up services - not covered in by this module that uses the VPN. - ''; - example = [46382 38473]; - }; - }; - }; - - config = mkIf cfg.enable { - assertions = [ - { - assertion = cfg.vpn.enable -> cfg.vpn.wgConf != null; - message = '' - The nixarr.vpn.enable option requires the nixarr.vpn.wgConf option - to be set, but it was not. - ''; - } - ]; - - # TODO: move this to modules, at least the "*Arrs"... - users.groups = { - media = {}; - streamer = {}; - torrenter = {}; - }; - users.users = { - streamer = { - isSystemUser = true; - group = "streamer"; - }; - torrenter = { - isSystemUser = true; - group = "torrenter"; - }; - }; - - systemd.tmpfiles.rules = [ - # Media dirs - "d '${cfg.mediaDir}' 0775 root media - -" - "d '${cfg.mediaDir}/library' 0775 streamer media - -" - "d '${cfg.mediaDir}/library/shows' 0775 streamer media - -" - "d '${cfg.mediaDir}/library/movies' 0775 streamer media - -" - "d '${cfg.mediaDir}/library/music' 0775 streamer media - -" - "d '${cfg.mediaDir}/library/books' 0775 streamer media - -" - "d '${cfg.mediaDir}/torrents' 0755 torrenter media - -" - "d '${cfg.mediaDir}/torrents/.incomplete' 0755 torrenter media - -" - "d '${cfg.mediaDir}/torrents/.watch' 0755 torrenter media - -" - "d '${cfg.mediaDir}/torrents/manual' 0755 torrenter media - -" - "d '${cfg.mediaDir}/torrents/liadarr' 0755 torrenter media - -" - "d '${cfg.mediaDir}/torrents/radarr' 0755 torrenter media - -" - "d '${cfg.mediaDir}/torrents/sonarr' 0755 torrenter media - -" - "d '${cfg.mediaDir}/torrents/readarr' 0755 torrenter media - -" - ]; - - # TODO: wtf to do about openports - vpnnamespaces.wg = { - enable = cfg.vpn.enable ; - accessibleFrom = [ - "192.168.1.0/24" - "127.0.0.1" - ]; - wireguardConfigFile = cfg.vpn.wgConf; - }; - - # TODO: openports - systemd.services.vpn-test-service = { - enable = cfg.vpn.vpnTestService.enable; - vpnconfinement = { - enable = true; - vpnnamespace = "wg"; - }; - - script = let - vpn-test = pkgs.writeShellApplication { - name = "vpn-test"; - - runtimeInputs = with pkgs; [util-linux unixtools.ping coreutils curl bash libressl netcat-gnu openresolv dig]; - - text = '' - cd "$(mktemp -d)" - - # Print resolv.conf - echo "/etc/resolv.conf contains:" - cat /etc/resolv.conf - - # Query resolvconf - echo "resolvconf output:" - resolvconf -l - echo "" - - # Get ip - echo "Getting IP:" - curl -s ipinfo.io - - echo -ne "DNS leak test:" - curl -s https://raw.githubusercontent.com/macvk/dnsleaktest/b03ab54d574adbe322ca48cbcb0523be720ad38d/dnsleaktest.sh -o dnsleaktest.sh - chmod +x dnsleaktest.sh - ./dnsleaktest.sh - '' + (if cfg.vpn.vpnTestService.port != null then '' - echo "starting netcat on port ${builtins.toString cfg.vpn.vpnTestService.port}:" - nc -vnlp ${builtins.toString cfg.vpn.vpnTestService.port} - '' else ""); - }; - in "${vpn-test}/bin/vpn-test"; - - bindsTo = ["netns@wg.service"]; - requires = ["network-online.target"]; - after = ["wg.service"]; - serviceConfig = { - #User = "torrenter"; - NetworkNamespacePath = "/var/run/netns/wg"; - BindReadOnlyPaths = ["/etc/netns/wg/resolv.conf:/etc/resolv.conf:norbind" "/data/test.file:/etc/test.file:norbind"]; - }; - }; - }; } diff --git a/nixarr/jellyfin/default.nix b/nixarr/jellyfin/default.nix index 69834b8..4d973d6 100644 --- a/nixarr/jellyfin/default.nix +++ b/nixarr/jellyfin/default.nix @@ -13,7 +13,7 @@ in with lib; { stateDir = mkOption { type = types.path; - default = "${nixarr.stateDir}/nixarr/jellyfin"; + default = "${nixarr.stateDir}/jellyfin"; description = "The state directory for Jellyfin."; }; diff --git a/nixarr/lidarr/default.nix b/nixarr/lidarr/default.nix index 94d4b6e..39c1425 100644 --- a/nixarr/lidarr/default.nix +++ b/nixarr/lidarr/default.nix @@ -12,7 +12,7 @@ in { stateDir = mkOption { type = types.path; - default = "${nixarr.stateDir}/nixarr/lidarr"; + default = "${nixarr.stateDir}/lidarr"; description = "The state directory for Lidarr"; }; diff --git a/nixarr/nixarr.nix b/nixarr/nixarr.nix new file mode 100644 index 0000000..d558ffd --- /dev/null +++ b/nixarr/nixarr.nix @@ -0,0 +1,240 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixarr; +in { + imports = [ + ./jellyfin + ./ddns + ./radarr + ./lidarr + ./readarr + ./sonarr + ./openssh + ./prowlarr + ./transmission + ../util + ]; + + options.nixarr = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether or not to enable the nixarr module. Has the following features: + + - **Run services through a VPN:** You can run any service that this module + supports through a VPN, fx `nixarr.transmission.vpn.enable = true;` + - **Automatic Directories, Users and Permissions:** The module automatically + creates directories and users for your media library. It also sets sane + permissions. + - **State Management:** All services support state management and all state + that they manage is located by default in `/data/.state/nixarr/*` + - **Optional Automatic Port Forwarding:** This module has a UPNP support that + lets services request ports from your router automatically, if you enable it. + + It is possible, _but not recommended_, to run the "*Arrs" behind a VPN, + because it can cause rate limiting issues. Generally, you should use + VPN on transmission and maybe jellyfin, depending on your setup. + + The following services are supported: + + - [Jellyfin](#nixarr.jellyfin.enable) + - [Lidarr](#nixarr.lidarr.enable) + - [Prowlarr](#nixarr.prowlarr.enable) + - [Radarr](#nixarr.radarr.enable) + - [Readarr](#nixarr.readarr.enable) + - [Sonarr](#nixarr.sonarr.enable) + - [Transmission](#nixarr.transmission.enable) + + Remember to read the options. + ''; + }; + + mediaDir = mkOption { + type = types.path; + default = "/data/media"; + description = '' + The location of the media directory for the services. + ''; + }; + + stateDir = mkOption { + type = types.path; + default = "/data/.state/nixarr"; + description = '' + The location of the state directory for the services. + ''; + }; + + vpn = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + **Required options:** [`nixarr.vpn.wgConf`](#nixarr.vpn.wgconf) + + Whether or not to enable VPN support for the services that nixarr + supports. + ''; + }; + + wgConf = mkOption { + type = types.nullOr types.path; + default = null; + description = "The path to the wireguard configuration file."; + }; + + vpnTestService = { + enable = mkEnableOption '' + the vpn test service. Useful for testing DNS leaks or if the VPN + port forwarding works correctly. + ''; + + port = mkOption { + type = with types; nullOr port; + default = null; + description = '' + The port that netcat listens to on the vpn test service. If set to + `null`, then netcat will not be started. + ''; + example = 58403; + }; + }; + + openTcpPorts = mkOption { + type = with types; listOf port; + default = []; + description = '' + What TCP ports to allow traffic from. You might need this if you're + port forwarding on your VPN provider and you're setting up services + not covered in by this module that uses the VPN. + ''; + example = [46382 38473]; + }; + + openUdpPorts = mkOption { + type = with types; listOf port; + default = []; + description = '' + What UDP ports to allow traffic from. You might need this if you're + port forwarding on your VPN provider and you're setting up services + not covered in by this module that uses the VPN. + ''; + example = [46382 38473]; + }; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = cfg.vpn.enable -> cfg.vpn.wgConf != null; + message = '' + The nixarr.vpn.enable option requires the nixarr.vpn.wgConf option + to be set, but it was not. + ''; + } + ]; + + # TODO: move this to modules, at least the "*Arrs"... + users.groups = { + media = {}; + streamer = {}; + torrenter = {}; + }; + users.users = { + streamer = { + isSystemUser = true; + group = "streamer"; + }; + torrenter = { + isSystemUser = true; + group = "torrenter"; + }; + }; + + systemd.tmpfiles.rules = [ + # Media dirs + "d '${cfg.mediaDir}' 0775 root media - -" + "d '${cfg.mediaDir}/library' 0775 streamer media - -" + "d '${cfg.mediaDir}/library/shows' 0775 streamer media - -" + "d '${cfg.mediaDir}/library/movies' 0775 streamer media - -" + "d '${cfg.mediaDir}/library/music' 0775 streamer media - -" + "d '${cfg.mediaDir}/library/books' 0775 streamer media - -" + "d '${cfg.mediaDir}/torrents' 0755 torrenter media - -" + "d '${cfg.mediaDir}/torrents/.incomplete' 0755 torrenter media - -" + "d '${cfg.mediaDir}/torrents/.watch' 0755 torrenter media - -" + "d '${cfg.mediaDir}/torrents/manual' 0755 torrenter media - -" + "d '${cfg.mediaDir}/torrents/liadarr' 0755 torrenter media - -" + "d '${cfg.mediaDir}/torrents/radarr' 0755 torrenter media - -" + "d '${cfg.mediaDir}/torrents/sonarr' 0755 torrenter media - -" + "d '${cfg.mediaDir}/torrents/readarr' 0755 torrenter media - -" + ]; + + # TODO: wtf to do about openports + vpnnamespaces.wg = { + enable = cfg.vpn.enable ; + accessibleFrom = [ + "192.168.1.0/24" + "127.0.0.1" + ]; + wireguardConfigFile = cfg.vpn.wgConf; + }; + + # TODO: openports + systemd.services.vpn-test-service = { + enable = cfg.vpn.vpnTestService.enable; + vpnconfinement = { + enable = true; + vpnnamespace = "wg"; + }; + + script = let + vpn-test = pkgs.writeShellApplication { + name = "vpn-test"; + + runtimeInputs = with pkgs; [util-linux unixtools.ping coreutils curl bash libressl netcat-gnu openresolv dig]; + + text = '' + cd "$(mktemp -d)" + + # Print resolv.conf + echo "/etc/resolv.conf contains:" + cat /etc/resolv.conf + + # Query resolvconf + echo "resolvconf output:" + resolvconf -l + echo "" + + # Get ip + echo "Getting IP:" + curl -s ipinfo.io + + echo -ne "DNS leak test:" + curl -s https://raw.githubusercontent.com/macvk/dnsleaktest/b03ab54d574adbe322ca48cbcb0523be720ad38d/dnsleaktest.sh -o dnsleaktest.sh + chmod +x dnsleaktest.sh + ./dnsleaktest.sh + '' + (if cfg.vpn.vpnTestService.port != null then '' + echo "starting netcat on port ${builtins.toString cfg.vpn.vpnTestService.port}:" + nc -vnlpu ${builtins.toString cfg.vpn.vpnTestService.port} + '' else ""); + }; + in "${vpn-test}/bin/vpn-test"; + + bindsTo = ["netns@wg.service"]; + requires = ["network-online.target"]; + after = ["wg.service"]; + serviceConfig = { + #User = "torrenter"; + NetworkNamespacePath = "/var/run/netns/wg"; + BindReadOnlyPaths = ["/etc/netns/wg/resolv.conf:/etc/resolv.conf:norbind" "/data/test.file:/etc/test.file:norbind"]; + }; + }; + }; +} diff --git a/nixarr/prowlarr/default.nix b/nixarr/prowlarr/default.nix index 7d9bd70..2040544 100644 --- a/nixarr/prowlarr/default.nix +++ b/nixarr/prowlarr/default.nix @@ -18,7 +18,7 @@ in { stateDir = mkOption { type = types.path; - default = "${nixarr.stateDir}/nixarr/prowlarr"; + default = "${nixarr.stateDir}/prowlarr"; description = "The state directory for Prowlarr."; }; @@ -48,8 +48,6 @@ in { "d '${cfg.stateDir}' 0700 prowlarr root - -" ]; - users.groups.prowlarr = {}; - util-nixarr.services.prowlarr = { enable = true; dataDir = cfg.stateDir; diff --git a/nixarr/prowlarr/prowlarr-module/default.nix b/nixarr/prowlarr/prowlarr-module/default.nix index 7655ff4..48c919f 100644 --- a/nixarr/prowlarr/prowlarr-module/default.nix +++ b/nixarr/prowlarr/prowlarr-module/default.nix @@ -70,6 +70,8 @@ in { }; }; - users.groups = mkIf (cfg.group == "prowlarr") {}; + users.groups = mkIf (cfg.group == "prowlarr") { + prowlarr = { }; + }; }; } diff --git a/nixarr/radarr/default.nix b/nixarr/radarr/default.nix index a20176a..1957555 100644 --- a/nixarr/radarr/default.nix +++ b/nixarr/radarr/default.nix @@ -14,7 +14,7 @@ in { stateDir = mkOption { type = types.path; - default = "${nixarr.stateDir}/nixarr/radarr"; + default = "${nixarr.stateDir}/radarr"; description = "The state directory for radarr."; }; diff --git a/nixarr/readarr/default.nix b/nixarr/readarr/default.nix index a79d4b6..9cd83e6 100644 --- a/nixarr/readarr/default.nix +++ b/nixarr/readarr/default.nix @@ -12,7 +12,7 @@ in { stateDir = mkOption { type = types.path; - default = "${nixarr.stateDir}/nixarr/readarr"; + default = "${nixarr.stateDir}/readarr"; description = "The state directory for Readarr"; }; diff --git a/nixarr/transmission/cross-seed/default.nix b/nixarr/transmission/cross-seed/default.nix index b5278e0..5e32c4d 100644 --- a/nixarr/transmission/cross-seed/default.nix +++ b/nixarr/transmission/cross-seed/default.nix @@ -8,7 +8,37 @@ with lib; let cfg = config.util-nixarr.services.cross-seed; settingsFormat = pkgs.formats.json {}; settingsFile = settingsFormat.generate "settings.json" cfg.settings; - cross-seedPkg = import ../../../pkgs/cross-seed { inherit (pkgs) stdenv lib fetchFromGitHub; }; + cross-seedPkg = pkgs.callPackage ../../../pkgs/cross-seed {}; + configJs = pkgs.writeText "config.js" '' + // Loads a json.config + "use strict"; + const fs = require('fs'); + + const jsonPath = '${cfg.dataDir}/config.json' + + // Synchronously read the JSON-configuration file + const configFileContent = fs.readFileSync(jsonPath, { encoding: 'utf8' }); + + // Parse the JSON content into a JavaScript object + let config = JSON.parse(configFileContent); + + // Function to recursively replace null values with undefined + /* + function replaceNullWithUndefined(obj) { + Object.keys(obj).forEach(key => { + if (obj[key] === null) { + obj[key] = undefined; + } else if (typeof obj[key] === 'object') { + replaceNullWithUndefined(obj[key]); + } + }); + } + replaceNullWithUndefined(config); + */ + + // Export the configuration object + module.exports = config; + ''; in { options = { util-nixarr.services.cross-seed = { @@ -52,9 +82,23 @@ in { }; config = mkIf cfg.enable { + assertions = [ + { + assertion = cfg.enable -> cfg.settings.outputDir != null; + message = '' + The settings.outputDir must be set if cross-seed is enabled. + ''; + } + ]; + systemd.tmpfiles.rules = [ + "L+ '${cfg.dataDir}'/config.js - - - - ${configJs}" "d '${cfg.dataDir}' 0700 ${cfg.user} ${cfg.group} - -" - ]; + ] ++ ( + if cfg.settings.outputDir != null then + [ "d '${cfg.settings.outputDir}' 0755 ${cfg.user} ${cfg.group} - -" ] + else [] + ); systemd.services.cross-seed = { description = "cross-seed"; @@ -73,7 +117,7 @@ in { Type = "simple"; User = cfg.user; Group = cfg.group; - ExecStart = "${getExe cross-seedPkg} daemon"; + ExecStart = "${cross-seedPkg}/bin/cross-seed daemon"; Restart = "on-failure"; }; }; @@ -85,6 +129,8 @@ in { }; }; - users.groups = mkIf (cfg.group == "cross-seed") {}; + users.groups = mkIf (cfg.group == "cross-seed") { + cross-seed = { }; + }; }; } diff --git a/nixarr/transmission/default.nix b/nixarr/transmission/default.nix index 367bba9..f77b626 100644 --- a/nixarr/transmission/default.nix +++ b/nixarr/transmission/default.nix @@ -9,12 +9,25 @@ with lib; let nixarr = config.nixarr; cfg-cross-seed = config.nixarr.transmission.privateTrackers.cross-seed; transmissionCrossSeedScript = with builtins; pkgs.writeShellApplication { - name = "mk-cross-seed-credentials"; + name = "transmission-cross-seed-script"; runtimeInputs = with pkgs; [ curl ]; text = '' - curl -XPOST http://localhost:2468/api/webhook?apikey=YOUR_API_KEY --data-urlencode "infoHash=$TR_TORRENT_HASH" + PROWLARR_API_KEY=$(cat prowlarr-api-key) + curl -XPOST http://localhost:2468/api/webhook?apikey="$PROWLARR_API_KEY" --data-urlencode "infoHash=$TR_TORRENT_HASH" + ''; + }; + importProwlarrApi = with builtins; pkgs.writeShellApplication { + name = "import-prowlarr-api"; + + runtimeInputs = with pkgs; [ yq ]; + + text = '' + touch ${cfg.stateDir}/prowlarr-api-key + chmod 400 ${cfg.stateDir}/prowlarr-api-key + chown torrenter ${cfg.stateDir}/prowlarr-api-key + xq -r '.Config.ApiKey' "${nixarr.prowlarr.stateDir}/config.xml" > "${cfg.stateDir}/prowlarr-api-key" ''; }; mkCrossSeedCredentials = with builtins; pkgs.writeShellApplication { @@ -24,14 +37,17 @@ with lib; let text = "INDEX_LINKS=(" - + strings.concatMapStringsSep " " toString cfg.privateTrackers.cross-seed.indexIds + + (strings.concatMapStringsSep " " toString cfg.privateTrackers.cross-seed.indexIds) + ")" - '' + + "\n" + + '' TMP_JSON=$(mktemp) CRED_FILE="/run/secrets/cross-seed/credentialsFile.json" - PROWLARR_API_KEY=$(xq '.Config.ApiKey' "${nixarr.prowlarr.stateDir}/config.xml") - CRED_DIR=$(dirname "$filePath") + PROWLARR_API_KEY=$(xq -r '.Config.ApiKey' "${nixarr.prowlarr.stateDir}/config.xml") + # shellcheck disable=SC2034 + CRED_DIR=$(dirname "$CRED_FILE") + mkdir -p "$CRED_DIR" echo '{}' > "$CRED_FILE" chmod 400 "$CRED_FILE" chown "${config.util-nixarr.services.cross-seed.user}" "$CRED_FILE" @@ -49,7 +65,7 @@ in { stateDir = mkOption { type = types.path; - default = "${nixarr.stateDir}/nixarr/transmission"; + default = "${nixarr.stateDir}/transmission"; description = '' The state directory for Transmission. ''; @@ -103,7 +119,7 @@ in { stateDir = mkOption { type = types.path; - default = "${nixarr.stateDir}/nixarr/cross-seed"; + default = "${nixarr.stateDir}/cross-seed"; description = '' The state directory for Transmission. ''; @@ -212,8 +228,8 @@ in { #group = "media"; settings = { torrentDir = "${nixarr.mediaDir}/torrents"; - outputDir = "${nixarr.mediaDir}/torrents/cross-seed"; - transmissionRpcUrl = "http://transmission:${builtins.toString cfg.uiPort}/transmission/rpc"; + outputDir = "${nixarr.mediaDir}/torrents/.cross-seed"; + transmissionRpcUrl = "http://localhost:${builtins.toString cfg.uiPort}/transmission/rpc"; rssCadence = "20 minutes"; # Enable infrequent periodic searches @@ -224,11 +240,16 @@ in { }; # Run as root in case that the cfg.credentialsFile is not readable by cross-seed systemd.services.cross-seed.serviceConfig = mkIf cfg-cross-seed.enable { - ExecStartPre = [(mkBefore - ("+" + (getExe mkCrossSeedCredentials)) + ExecStartPre = mkBefore [( + "+" + "${mkCrossSeedCredentials}/bin/mk-cross-seed-credentials" )]; }; + systemd.services.transmission.serviceConfig = mkIf cfg-cross-seed.enable { + ExecStartPre = mkBefore [( + "+" + "${importProwlarrApi}/bin/import-prowlarr-api" + )]; + }; services.transmission = { enable = true; user = "torrenter"; @@ -270,7 +291,9 @@ in { anti-brute-force-threshold = 10; script-torrent-done-enabled = cfg-cross-seed.enable; - script-torrent-done-filename = if cfg-cross-seed.enable then transmissionCrossSeedScript else null; + script-torrent-done-filename = if cfg-cross-seed.enable then + "${transmissionCrossSeedScript}/bin/transmission-cross-seed-script" + else null; message-level = if cfg.messageLevel == "none" diff --git a/pkgs/cross-seed/default.nix b/pkgs/cross-seed/default.nix index b39f28a..2a69754 100644 --- a/pkgs/cross-seed/default.nix +++ b/pkgs/cross-seed/default.nix @@ -1,75 +1,22 @@ -{ - config, - pkgs, - lib, - ... -}: -with lib; let - cfg = config.util-nixarr.services.prowlarr; - settingsFormat = pkgs.formats.json {}; - settingsFile = settingsFormat.generate "settings.json" cfg.settings; - cross-seedPkg = import ../../../pkgs/cross-seed { inherit (pkgs) stdenv lib fetchFromGitHub; }; -in { - options = { - util-nixarr.services.prowlarr = { - enable = mkEnableOption "cross-seed"; +{ lib, buildNpmPackage, fetchFromGitHub }: - configFile = mkOption { - type = with types; nullOr path; - default = null; - example = "/var/lib/secrets/cross-seed/settings.json"; - description = ""; - }; +buildNpmPackage rec { + pname = "cross-seed"; + version = "5.9.2"; - dataDir = mkOption { - type = types.path; - default = "/var/lib/cross-seed"; - }; - - user = mkOption { - type = types.str; - default = "cross-seed"; - description = "User account under which cross-seed runs."; - }; - - group = mkOption { - type = types.str; - default = "cross-seed"; - description = "Group under which cross-seed runs."; - }; - }; + src = fetchFromGitHub { + owner = "cross-seed"; + repo = pname; + rev = "v${version}"; + hash = "sha256-E0AlsFV9RP01YVwjw6ZQ8Lf1IVyuudxrb5oJ61EfIyo="; }; - config = mkIf cfg.enable { - systemd.tmpfiles.rules = [ - "d '${cfg.dataDir}' 0700 ${cfg.user} ${cfg.group} - -" - ]; - - systemd.services.prowlarr = { - description = "cross-seed"; - after = ["network.target"]; - wantedBy = ["multi-user.target"]; - - environment.CONFIG_DIR = cfg.dataDir; - - serviceConfig = { - ExecStartPre = [("+" + pkgs.writeShellScript "transmission-prestart" '' - mv ${cfg.configFile} ${cfg.dataDir} - '')]; - Type = "simple"; - User = cfg.user; - Group = cfg.group; - ExecStart = "${getExe cross-seedPkg} daemon"; - Restart = "on-failure"; - }; - }; - - users.users = mkIf (cfg.user == "cross-seed") { - cross-seed = { - group = cfg.group; - }; - }; + npmDepsHash = "sha256-hZKLv+bzRFiMjNemydCUC1d7xul7Mm+vOPtCUD7p9XQ="; - users.groups = mkIf (cfg.group == "cross-seed") {}; + meta = with lib; { + description = "cross-seed is an app designed to help you download torrents that you can cross seed based on your existing torrents"; + homepage = "https://www.cross-seed.org"; + license = licenses.asl20; + maintainers = with maintainers; [ rasmus-kirk ]; }; }