Compare commits

..

4 Commits

Author SHA1 Message Date
ae6915c204 feat(media): embed recyclarr config with codec scoring, enable qbittorrent auto-tmm 2026-02-18 22:01:49 -05:00
b8b8dd58e1 jellyseerr: disable caddy_auth 2026-02-18 21:02:53 -05:00
88b506b08c feat(media): add recyclarr service for automated TRaSH Guides sync
Add systemd oneshot + daily timer to sync TRaSH Guides quality profiles
and custom formats to Radarr/Sonarr via recyclarr.
2026-02-18 21:02:53 -05:00
93a7645d3f feat(media): add arr stack (sonarr, radarr, bazarr, prowlarr, jellyseerr) 2026-02-18 21:02:47 -05:00

View File

@@ -6,117 +6,121 @@
... ...
}: }:
let let
configTemplate = (pkgs.formats.yaml {}).generate "recyclarr.yml" { radarrPort = builtins.toString service_configs.ports.radarr;
radarr.movies = { sonarrPort = builtins.toString service_configs.ports.sonarr;
base_url = "http://localhost:${builtins.toString service_configs.ports.radarr}";
api_key = "RADARR_API_KEY_PLACEHOLDER";
include = [ configTemplate = pkgs.writeText "recyclarr.yml" ''
{ template = "radarr-quality-definition-movie"; } radarr:
{ template = "radarr-quality-profile-remux-web-2160p"; } movies:
{ template = "radarr-custom-formats-remux-web-2160p"; } base_url: http://localhost:${radarrPort}
]; api_key: RADARR_API_KEY_PLACEHOLDER
quality_profiles = [{ include:
name = "Remux + WEB 2160p"; - template: radarr-quality-definition-movie
upgrade = { - template: radarr-quality-profile-remux-web-2160p
allowed = true; - template: radarr-custom-formats-remux-web-2160p
until_quality = "Remux-2160p";
};
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 = [ # Override template to also allow 1080p (2160p still prioritized via cutoff)
# Upscaled quality_profiles:
{ - name: Remux + WEB 2160p
trash_ids = [ "bfd8eb01832d646a0a89c4deb46f8564" ]; upgrade:
assign_scores_to = [{ name = "Remux + WEB 2160p"; score = -10000; }]; allowed: true
} until_quality: Remux-2160p
# x265 (HD) - override template -10000 penalty qualities:
{ - name: Remux-2160p
trash_ids = [ "dc98083864ea246d05a42df0d05f81cc" ]; - name: WEB 2160p
assign_scores_to = [{ name = "Remux + WEB 2160p"; score = 0; }]; qualities:
} - WEBDL-2160p
# x265 (no HDR/DV) - override template -10000 penalty - WEBRip-2160p
{ - name: Remux-1080p
trash_ids = [ "839bea857ed2c0a8e084f3cbdbd65ecb" ]; - name: Bluray-1080p
assign_scores_to = [{ name = "Remux + WEB 2160p"; score = 0; }]; - name: WEB 1080p
} qualities:
]; - WEBDL-1080p
}; - WEBRip-1080p
- name: HDTV-1080p
sonarr.series = { custom_formats:
base_url = "http://localhost:${builtins.toString service_configs.ports.sonarr}"; - trash_ids:
api_key = "SONARR_API_KEY_PLACEHOLDER"; - bfd8eb01832d646a0a89c4deb46f8564 # Upscaled
assign_scores_to:
- name: Remux + WEB 2160p
score: -10000
# Codec preferences: allow x265/HEVC (override template -10000 penalties)
- trash_ids:
- dc98083864ea246d05a42df0d05f81cc # x265 (HD)
assign_scores_to:
- name: Remux + WEB 2160p
score: 0
- trash_ids:
- 839bea857ed2c0a8e084f3cbdbd65ecb # x265 (no HDR/DV)
assign_scores_to:
- name: Remux + WEB 2160p
score: 0
- trash_ids:
- cae4ca30163749b891686f95532519bd # AV1
assign_scores_to:
- name: Remux + WEB 2160p
score: 0
include = [ sonarr:
{ template = "sonarr-quality-definition-series"; } series:
{ template = "sonarr-v4-quality-profile-web-2160p"; } base_url: http://localhost:${sonarrPort}
{ template = "sonarr-v4-custom-formats-web-2160p"; } api_key: SONARR_API_KEY_PLACEHOLDER
];
quality_profiles = [{ include:
name = "WEB-2160p"; - template: sonarr-quality-definition-series
upgrade = { - template: sonarr-v4-quality-profile-web-2160p
allowed = true; - template: sonarr-v4-custom-formats-web-2160p
until_quality = "WEB 2160p";
};
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 = [ # Override template to also allow 1080p (2160p still prioritized via cutoff)
# Upscaled quality_profiles:
{ - name: WEB-2160p
trash_ids = [ "23297a736ca77c0fc8e70f8edd7ee56c" ]; upgrade:
assign_scores_to = [{ name = "WEB-2160p"; score = -10000; }]; allowed: true
} until_quality: WEB 2160p
# x265 (HD) - override template -10000 penalty qualities:
{ - name: WEB 2160p
trash_ids = [ "47435ece6b99a0b477caf360e79ba0bb" ]; qualities:
assign_scores_to = [{ name = "WEB-2160p"; score = 0; }]; - WEBDL-2160p
} - WEBRip-2160p
# x265 (no HDR/DV) - override template -10000 penalty - name: Bluray-1080p Remux
{ - name: Bluray-1080p
trash_ids = [ "9b64dff695c2115facf1b6ea59c9bd07" ]; - name: WEB 1080p
assign_scores_to = [{ name = "WEB-2160p"; score = 0; }]; qualities:
} - WEBDL-1080p
]; - WEBRip-1080p
}; - name: HDTV-1080p
};
custom_formats:
- trash_ids:
- 23297a736ca77c0fc8e70f8edd7ee56c # Upscaled
assign_scores_to:
- name: WEB-2160p
score: -10000
# Codec preferences: allow x265/HEVC (override template -10000 penalties)
- trash_ids:
- 47435ece6b99a0b477caf360e79ba0bb # x265 (HD)
assign_scores_to:
- name: WEB-2160p
score: 0
- trash_ids:
- 9b64dff695c2115facf1b6ea59c9bd07 # x265 (no HDR/DV)
assign_scores_to:
- name: WEB-2160p
score: 0
- trash_ids:
- 15a05bc7c1a36e2b57fd628f8977e2fc # AV1
assign_scores_to:
- name: WEB-2160p
score: 0
'';
dataDir = service_configs.recyclarr.dataDir; dataDir = service_configs.recyclarr.dataDir;
radarrConfig = "${service_configs.radarr.dataDir}/config.xml"; radarrConfig = "${service_configs.radarr.dataDir}/config.xml";
sonarrConfig = "${service_configs.sonarr.dataDir}/config.xml"; sonarrConfig = "${service_configs.sonarr.dataDir}/config.xml";
user = "recyclarr"; # Script to inject API keys from sonarr/radarr config.xml at runtime
group = "recyclarr";
# Runs as root (via + prefix) to read API keys from config.xml, then chowns output
generateConfig = pkgs.writeShellScript "recyclarr-generate-config" '' generateConfig = pkgs.writeShellScript "recyclarr-generate-config" ''
RADARR_KEY=$(${pkgs.gnugrep}/bin/grep -oP '(?<=<ApiKey>)[^<]+' ${radarrConfig}) RADARR_KEY=$(${pkgs.gnugrep}/bin/grep -oP '(?<=<ApiKey>)[^<]+' ${radarrConfig})
SONARR_KEY=$(${pkgs.gnugrep}/bin/grep -oP '(?<=<ApiKey>)[^<]+' ${sonarrConfig}) SONARR_KEY=$(${pkgs.gnugrep}/bin/grep -oP '(?<=<ApiKey>)[^<]+' ${sonarrConfig})
@@ -124,7 +128,6 @@ let
-e "s/RADARR_API_KEY_PLACEHOLDER/$RADARR_KEY/" \ -e "s/RADARR_API_KEY_PLACEHOLDER/$RADARR_KEY/" \
-e "s/SONARR_API_KEY_PLACEHOLDER/$SONARR_KEY/" \ -e "s/SONARR_API_KEY_PLACEHOLDER/$SONARR_KEY/" \
${configTemplate} > ${dataDir}/recyclarr.yml ${configTemplate} > ${dataDir}/recyclarr.yml
chown ${user}:${group} ${dataDir}/recyclarr.yml
''; '';
in in
{ {
@@ -135,8 +138,8 @@ in
]; ];
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"d ${dataDir} 0755 ${user} ${group} -" "d ${dataDir} 0755 root root -"
"d ${dataDir}/data 0755 ${user} ${group} -" "d ${dataDir}/data 0755 root root -"
]; ];
systemd.services.recyclarr = { systemd.services.recyclarr = {
@@ -145,21 +148,11 @@ in
wants = [ "network-online.target" ]; wants = [ "network-online.target" ];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
User = user; ExecStartPre = "${generateConfig}";
Group = group; ExecStart = "${pkgs.recyclarr}/bin/recyclarr sync --config ${dataDir}/recyclarr.yml --app-data ${dataDir}/data";
ExecStartPre = "+${generateConfig}";
ExecStart = "${lib.getExe pkgs.recyclarr} sync --config ${dataDir}/recyclarr.yml --app-data ${dataDir}/data";
}; };
}; };
users.users.${user} = {
isSystemUser = true;
group = group;
home = dataDir;
};
users.groups.${group} = {};
systemd.timers.recyclarr = { systemd.timers.recyclarr = {
description = "Run Recyclarr daily"; description = "Run Recyclarr daily";
wantedBy = [ "timers.target" ]; wantedBy = [ "timers.target" ];