Files
nixos/services/arr/recyclarr.nix
2026-04-01 20:37:18 -04:00

261 lines
7.4 KiB
Nix

{
pkgs,
config,
service_configs,
lib,
...
}:
let
radarrConfig = "${service_configs.radarr.dataDir}/config.xml";
sonarrConfig = "${service_configs.sonarr.dataDir}/config.xml";
configPath = "/var/lib/recyclarr/config.json";
# Runs as root (via + prefix) after the NixOS module writes config.json.
# Extracts API keys from radarr/sonarr config.xml and injects them via jq.
injectApiKeys = pkgs.writeShellScript "recyclarr-inject-api-keys" ''
RADARR_KEY=$(${lib.getExe pkgs.gnugrep} -oP '(?<=<ApiKey>)[^<]+' ${radarrConfig})
SONARR_KEY=$(${lib.getExe pkgs.gnugrep} -oP '(?<=<ApiKey>)[^<]+' ${sonarrConfig})
${pkgs.jq}/bin/jq \
--arg rk "$RADARR_KEY" \
--arg sk "$SONARR_KEY" \
'.radarr.movies.api_key = $rk | .sonarr.series.api_key = $sk' \
${configPath} > ${configPath}.tmp
mv ${configPath}.tmp ${configPath}
chown recyclarr:recyclarr ${configPath}
'';
in
{
imports = [
(lib.serviceMountWithZpool "recyclarr" service_configs.zpool_ssds [
service_configs.recyclarr.dataDir
])
];
systemd.tmpfiles.rules = [
"d ${service_configs.recyclarr.dataDir} 0755 recyclarr recyclarr -"
];
services.recyclarr = {
enable = true;
command = "sync";
schedule = "daily";
user = "recyclarr";
group = "recyclarr";
configuration = {
radarr.movies = {
base_url = "http://localhost:${builtins.toString service_configs.ports.private.radarr.port}";
include = [
{ template = "radarr-quality-definition-movie"; }
{ template = "radarr-quality-profile-remux-web-2160p"; }
{ template = "radarr-custom-formats-remux-web-2160p"; }
];
quality_profiles = [
{
name = "Remux + WEB 2160p";
min_format_score = 0;
reset_unmatched_scores = {
enabled = true;
};
upgrade = {
allowed = true;
until_quality = "Remux-2160p";
until_score = 10000;
};
quality_sort = "top";
qualities = [
{ name = "Remux-2160p"; }
{
name = "WEB 2160p";
qualities = [
"WEBDL-2160p"
"WEBRip-2160p"
];
}
{ name = "Remux-1080p"; }
{ name = "Bluray-1080p"; }
{
name = "WEB 1080p";
qualities = [
"WEBDL-1080p"
"WEBRip-1080p"
];
}
{ name = "HDTV-1080p"; }
];
}
];
custom_formats = [
# Upscaled
{
trash_ids = [ "bfd8eb01832d646a0a89c4deb46f8564" ];
assign_scores_to = [
{
name = "Remux + WEB 2160p";
score = -10000;
}
];
}
# x265 (HD) - override template -10000 penalty for non-2160p HEVC
{
trash_ids = [ "dc98083864ea246d05a42df0d05f81cc" ];
assign_scores_to = [
{
name = "Remux + WEB 2160p";
score = 0;
}
];
}
# x265 (no HDR/DV) - override template -10000 penalty for non-2160p HEVC
{
trash_ids = [ "839bea857ed2c0a8e084f3cbdbd65ecb" ];
assign_scores_to = [
{
name = "Remux + WEB 2160p";
score = 0;
}
];
}
# Codec ranking: AV1 (20) > HEVC (10) > AVC (0)
#
# Positive scores only -- nothing drops below min_format_score.
# AVC stays at 0 implicitly (no custom format adds or removes score).
{
trash_ids = [ "cae4ca30163749b891686f95532519bd" ]; # AV1
assign_scores_to = [
{
name = "Remux + WEB 2160p";
score = 20;
}
];
}
{
trash_ids = [ "9170d55c319f4fe40da8711ba9d8050d" ]; # x265
assign_scores_to = [
{
name = "Remux + WEB 2160p";
score = 10;
}
];
}
];
};
sonarr.series = {
base_url = "http://localhost:${builtins.toString service_configs.ports.private.sonarr.port}";
include = [
{ template = "sonarr-quality-definition-series"; }
{ template = "sonarr-v4-quality-profile-web-2160p"; }
{ template = "sonarr-v4-custom-formats-web-2160p"; }
];
quality_profiles = [
{
name = "WEB-2160p";
min_format_score = 0;
reset_unmatched_scores = {
enabled = true;
};
upgrade = {
allowed = true;
until_quality = "WEB 2160p";
until_score = 10000;
};
quality_sort = "top";
qualities = [
{
name = "WEB 2160p";
qualities = [
"WEBDL-2160p"
"WEBRip-2160p"
];
}
{ name = "Bluray-1080p Remux"; }
{ name = "Bluray-1080p"; }
{
name = "WEB 1080p";
qualities = [
"WEBDL-1080p"
"WEBRip-1080p"
];
}
{ name = "HDTV-1080p"; }
];
}
];
custom_formats = [
# Upscaled
{
trash_ids = [ "23297a736ca77c0fc8e70f8edd7ee56c" ];
assign_scores_to = [
{
name = "WEB-2160p";
score = -10000;
}
];
}
# x265 (HD) - override template -10000 penalty for non-2160p HEVC
{
trash_ids = [ "47435ece6b99a0b477caf360e79ba0bb" ];
assign_scores_to = [
{
name = "WEB-2160p";
score = 0;
}
];
}
# x265 (no HDR/DV) - override template -10000 penalty for non-2160p HEVC
{
trash_ids = [ "9b64dff695c2115facf1b6ea59c9bd07" ];
assign_scores_to = [
{
name = "WEB-2160p";
score = 0;
}
];
}
# Codec ranking: AV1 (20) > HEVC (10) > AVC (0)
#
# Positive scores only -- nothing drops below min_format_score.
# AVC stays at 0 implicitly (no custom format adds or removes score).
{
trash_ids = [ "15a05bc7c1a36e2b57fd628f8977e2fc" ]; # AV1
assign_scores_to = [
{
name = "WEB-2160p";
score = 20;
}
];
}
{
trash_ids = [ "c9eafd50846d299b862ca9bb6ea91950" ]; # x265
assign_scores_to = [
{
name = "WEB-2160p";
score = 10;
}
];
}
];
};
};
};
# Re-sync immediately on deploy when the recyclarr config changes
systemd.services.recyclarr = {
restartTriggers = [ (builtins.toJSON config.services.recyclarr.configuration) ];
after = [
"network-online.target"
"radarr.service"
"sonarr.service"
];
wants = [ "network-online.target" ];
serviceConfig.ExecStartPre = [ "+${injectApiKeys}" ];
};
}