This repository has been archived on 2026-04-18. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
server-config/services/arr/arr-search.nix

116 lines
3.4 KiB
Nix

{
pkgs,
lib,
service_configs,
...
}:
let
radarrConfig = "${service_configs.radarr.dataDir}/config.xml";
sonarrConfig = "${service_configs.sonarr.dataDir}/config.xml";
radarrUrl = "http://localhost:${builtins.toString service_configs.ports.private.radarr.port}";
sonarrUrl = "http://localhost:${builtins.toString service_configs.ports.private.sonarr.port}";
curl = "${pkgs.curl}/bin/curl";
jq = "${pkgs.jq}/bin/jq";
# Max items to search per cycle per category (missing + cutoff) per app
maxPerCycle = 5;
searchScript = pkgs.writeShellScript "arr-search" ''
set -euo pipefail
RADARR_KEY=$(${lib.extractArrApiKey radarrConfig})
SONARR_KEY=$(${lib.extractArrApiKey sonarrConfig})
search_radarr() {
local endpoint="$1"
local label="$2"
local ids
ids=$(${curl} -sf --max-time 30 \
-H "X-Api-Key: $RADARR_KEY" \
"${radarrUrl}/api/v3/wanted/$endpoint?page=1&pageSize=${builtins.toString maxPerCycle}&monitored=true&sortKey=title&sortDirection=ascending" \
| ${jq} -r '.records[].id // empty')
if [ -z "$ids" ]; then
echo "radarr: no $label items"
return
fi
local id_array
id_array=$(echo "$ids" | ${jq} -Rs '[split("\n") | .[] | select(. != "") | tonumber]')
echo "radarr: searching $label: $id_array"
${curl} -sf --max-time 60 \
-H "X-Api-Key: $RADARR_KEY" \
-H "Content-Type: application/json" \
-X POST "${radarrUrl}/api/v3/command" \
-d "{\"name\": \"MoviesSearch\", \"movieIds\": $id_array}" > /dev/null
}
search_sonarr() {
local endpoint="$1"
local label="$2"
local series_ids
series_ids=$(${curl} -sf --max-time 30 \
-H "X-Api-Key: $SONARR_KEY" \
"${sonarrUrl}/api/v3/wanted/$endpoint?page=1&pageSize=${builtins.toString maxPerCycle}&monitored=true&sortKey=title&sortDirection=ascending&includeSeries=true" \
| ${jq} -r '[.records[].seriesId] | unique | .[] // empty')
if [ -z "$series_ids" ]; then
echo "sonarr: no $label items"
return
fi
# search per series (sonarr searches by series, not episode)
for sid in $series_ids; do
echo "sonarr: searching $label series $sid"
${curl} -sf --max-time 60 \
-H "X-Api-Key: $SONARR_KEY" \
-H "Content-Type: application/json" \
-X POST "${sonarrUrl}/api/v3/command" \
-d "{\"name\": \"SeriesSearch\", \"seriesId\": $sid}" > /dev/null
done
}
echo "=== arr-search $(date -Iseconds) ==="
search_radarr "missing" "missing"
search_radarr "cutoff" "cutoff-unmet"
search_sonarr "missing" "missing"
search_sonarr "cutoff" "cutoff-unmet"
echo "=== done ==="
'';
in
{
systemd.services.arr-search = {
description = "Search for missing and cutoff-unmet media in Radarr/Sonarr";
after = [
"network-online.target"
"radarr.service"
"sonarr.service"
];
wants = [ "network-online.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "+${searchScript}"; # + prefix: runs as root to read API keys from config.xml
TimeoutSec = 300;
};
};
systemd.timers.arr-search = {
description = "Periodically search for missing and cutoff-unmet media";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "*-*-* 03:00:00"; # daily at 3 AM
Persistent = true; # run on boot if missed
RandomizedDelaySec = "30m";
};
};
}