phase 2: promote services/, tests/, patches/, lib/, scripts/
This commit is contained in:
224
services/arr/recyclarr.nix
Normal file
224
services/arr/recyclarr.nix
Normal file
@@ -0,0 +1,224 @@
|
||||
{
|
||||
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.extractArrApiKey radarrConfig})
|
||||
SONARR_KEY=$(${lib.extractArrApiKey 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}";
|
||||
|
||||
# Recyclarr is the sole authority for custom formats and scores.
|
||||
# Overwrite any manually-created CFs and delete stale ones.
|
||||
replace_existing_custom_formats = true;
|
||||
delete_old_custom_formats = true;
|
||||
|
||||
include = [
|
||||
{ template = "radarr-quality-definition-movie"; }
|
||||
{ template = "radarr-quality-profile-remux-web-2160p"; }
|
||||
{ template = "radarr-custom-formats-remux-web-2160p"; }
|
||||
];
|
||||
|
||||
# Group WEB 2160p with 1080p in the same quality tier so custom
|
||||
# format scores -- not quality ranking -- decide the winner.
|
||||
# Native 4K with HDR/DV from good release groups scores high and
|
||||
# wins; AI upscales get -10000 from the Upscaled CF and are
|
||||
# blocked by min_format_score. Untagged upscales from unknown
|
||||
# groups (score ~0) lose to well-scored 1080p (Tier 01 = +1750).
|
||||
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;
|
||||
};
|
||||
qualities = [
|
||||
{ name = "Remux-2160p"; }
|
||||
{
|
||||
name = "WEB/Bluray";
|
||||
qualities = [
|
||||
"WEBDL-2160p"
|
||||
"WEBRip-2160p"
|
||||
"Remux-1080p"
|
||||
"Bluray-1080p"
|
||||
"WEBDL-1080p"
|
||||
"WEBRip-1080p"
|
||||
];
|
||||
}
|
||||
{ name = "HDTV-1080p"; }
|
||||
{ name = "Bluray-720p"; }
|
||||
{
|
||||
name = "WEB 720p";
|
||||
qualities = [
|
||||
"WEBDL-720p"
|
||||
"WEBRip-720p"
|
||||
];
|
||||
}
|
||||
{ name = "HDTV-720p"; }
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
custom_formats = [
|
||||
# DV (w/o HDR fallback) - block releases with DV that lack HDR10 fallback
|
||||
{
|
||||
trash_ids = [ "923b6abef9b17f937fab56cfcf89e1f1" ];
|
||||
assign_scores_to = [
|
||||
{ name = "Remux + WEB 2160p"; }
|
||||
];
|
||||
}
|
||||
# Upscaled - block AI upscales and other upscaled-to-2160p releases
|
||||
{
|
||||
trash_ids = [ "bfd8eb01832d646a0a89c4deb46f8564" ];
|
||||
assign_scores_to = [
|
||||
{
|
||||
name = "Remux + WEB 2160p";
|
||||
score = -10000;
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
sonarr.series = {
|
||||
base_url = "http://localhost:${builtins.toString service_configs.ports.private.sonarr.port}";
|
||||
|
||||
# Recyclarr is the sole authority for custom formats and scores.
|
||||
# Overwrite any manually-created CFs and delete stale ones.
|
||||
replace_existing_custom_formats = true;
|
||||
delete_old_custom_formats = true;
|
||||
|
||||
include = [
|
||||
{ template = "sonarr-quality-definition-series"; }
|
||||
{ template = "sonarr-v4-quality-profile-web-2160p"; }
|
||||
{ template = "sonarr-v4-custom-formats-web-2160p"; }
|
||||
];
|
||||
|
||||
# Group WEB 2160p with 1080p in the same quality tier so custom
|
||||
# format scores -- not quality ranking -- decide the winner.
|
||||
# Native 4K with HDR/DV from good release groups scores high and
|
||||
# wins; AI upscales get -10000 from the Upscaled CF and are
|
||||
# blocked by min_format_score. Untagged upscales from unknown
|
||||
# groups (score ~0) lose to well-scored 1080p (Tier 01 = +1750).
|
||||
quality_profiles = [
|
||||
{
|
||||
name = "WEB-2160p";
|
||||
min_format_score = 0;
|
||||
reset_unmatched_scores.enabled = true;
|
||||
upgrade = {
|
||||
allowed = true;
|
||||
until_quality = "WEB/Bluray";
|
||||
until_score = 10000;
|
||||
};
|
||||
qualities = [
|
||||
{
|
||||
name = "WEB/Bluray";
|
||||
qualities = [
|
||||
"WEBDL-2160p"
|
||||
"WEBRip-2160p"
|
||||
"Bluray-1080p Remux"
|
||||
"Bluray-1080p"
|
||||
"WEBDL-1080p"
|
||||
"WEBRip-1080p"
|
||||
];
|
||||
}
|
||||
{ name = "HDTV-1080p"; }
|
||||
{ name = "Bluray-720p"; }
|
||||
{
|
||||
name = "WEB 720p";
|
||||
qualities = [
|
||||
"WEBDL-720p"
|
||||
"WEBRip-720p"
|
||||
];
|
||||
}
|
||||
{ name = "HDTV-720p"; }
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
custom_formats = [
|
||||
# DV (w/o HDR fallback) - block releases with DV that lack HDR10 fallback
|
||||
{
|
||||
trash_ids = [ "9b27ab6498ec0f31a3353992e19434ca" ];
|
||||
assign_scores_to = [
|
||||
{ name = "WEB-2160p"; }
|
||||
];
|
||||
}
|
||||
# Upscaled - block AI upscales and other upscaled-to-2160p releases
|
||||
{
|
||||
trash_ids = [ "23297a736ca77c0fc8e70f8edd7ee56c" ];
|
||||
assign_scores_to = [
|
||||
{
|
||||
name = "WEB-2160p";
|
||||
score = -10000;
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Trigger immediate sync on deploy when recyclarr config changes.
|
||||
# restartTriggers on the oneshot service are unreliable (systemd may
|
||||
# no-op a restart of an inactive oneshot). Instead, embed a config
|
||||
# hash in the timer unit -- NixOS restarts changed timers reliably,
|
||||
# and OnActiveSec fires the sync within seconds.
|
||||
systemd.timers.recyclarr = {
|
||||
timerConfig.OnActiveSec = "5s";
|
||||
unitConfig.X-ConfigHash = builtins.hashString "sha256" (
|
||||
builtins.toJSON config.services.recyclarr.configuration
|
||||
);
|
||||
};
|
||||
|
||||
systemd.services.recyclarr = {
|
||||
after = [
|
||||
"network-online.target"
|
||||
"radarr.service"
|
||||
"sonarr.service"
|
||||
];
|
||||
wants = [ "network-online.target" ];
|
||||
serviceConfig.ExecStartPre = [ "+${injectApiKeys}" ];
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user