Skip to content

Commit 22f650a

Browse files
committed
configure full defaults for sonarr and radarr
1 parent 78c9aff commit 22f650a

4 files changed

Lines changed: 821 additions & 100 deletions

File tree

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
{lib, ...}:
2+
with lib; let
3+
inherit (lib) types;
4+
instanceType = types.submodule {
5+
options = {
6+
base_url = mkOption {
7+
type = types.str;
8+
description = "Base URL for the service instance (including port and URL base)";
9+
example = "http://127.0.0.1:8989";
10+
};
11+
12+
api_key = mkOption {
13+
type = types.oneOf [
14+
types.str
15+
(types.submodule {
16+
options._secret = mkOption {
17+
type = types.str;
18+
description = "Path to a file containing the secret value";
19+
};
20+
})
21+
];
22+
description = ''
23+
API key for the service.
24+
Can be a plain string or a secret reference using { _secret = "/path/to/file"; }
25+
'';
26+
example = literalExpression ''{ _secret = "/run/credentials/recyclarr.service/sonarr-api_key"; }'';
27+
};
28+
29+
delete_old_custom_formats = mkOption {
30+
type = types.bool;
31+
default = true;
32+
description = "Whether to delete custom formats in the service that are not defined in the configuration";
33+
};
34+
35+
replace_existing_custom_formats = mkOption {
36+
type = types.bool;
37+
default = true;
38+
description = "Whether to replace existing custom formats with configuration from Recyclarr";
39+
};
40+
41+
quality_definition = mkOption {
42+
type = types.nullOr (types.submodule {
43+
options.type = mkOption {
44+
type = types.enum ["series" "movie" "anime"];
45+
description = "Type of quality definition to use from TRaSH guides";
46+
};
47+
});
48+
default = null;
49+
description = "Quality definition configuration from TRaSH guides";
50+
};
51+
52+
media_naming = mkOption {
53+
type = types.nullOr (
54+
types.either
55+
# Sonarr media naming
56+
(types.submodule {
57+
options = {
58+
series = mkOption {
59+
type = types.nullOr types.str;
60+
default = null;
61+
description = "Series folder naming format ('default' for TRaSH guide default)";
62+
};
63+
64+
season = mkOption {
65+
type = types.nullOr types.str;
66+
default = null;
67+
description = "Season folder naming format ('default' for TRaSH guide default)";
68+
};
69+
70+
episodes = mkOption {
71+
type = types.nullOr (types.submodule {
72+
options = {
73+
rename = mkOption {
74+
type = types.bool;
75+
default = true;
76+
description = "Whether to rename episode files";
77+
};
78+
79+
standard = mkOption {
80+
type = types.nullOr types.str;
81+
default = null;
82+
description = "Standard episode naming format ('default' for TRaSH guide default)";
83+
};
84+
85+
daily = mkOption {
86+
type = types.nullOr types.str;
87+
default = null;
88+
description = "Daily episode naming format ('default' for TRaSH guide default)";
89+
};
90+
91+
anime = mkOption {
92+
type = types.nullOr types.str;
93+
default = null;
94+
description = "Anime episode naming format ('default' for TRaSH guide default)";
95+
};
96+
};
97+
});
98+
default = null;
99+
description = "Episode file naming configuration";
100+
};
101+
};
102+
})
103+
# Radarr media naming
104+
(types.submodule {
105+
options = {
106+
folder = mkOption {
107+
type = types.nullOr types.str;
108+
default = null;
109+
description = "Movie folder naming format ('default' for TRaSH guide default)";
110+
};
111+
112+
movie = mkOption {
113+
type = types.nullOr (types.submodule {
114+
options = {
115+
rename = mkOption {
116+
type = types.bool;
117+
default = true;
118+
description = "Whether to rename movie files";
119+
};
120+
121+
standard = mkOption {
122+
type = types.nullOr types.str;
123+
default = null;
124+
description = "Movie file naming format ('default' or 'standard' for TRaSH guide defaults)";
125+
};
126+
};
127+
});
128+
default = null;
129+
description = "Movie file naming configuration";
130+
};
131+
};
132+
})
133+
);
134+
default = null;
135+
description = ''
136+
Media naming configuration.
137+
For Sonarr: configures series, season, and episode naming.
138+
For Radarr: configures folder and movie naming.
139+
Use 'default' or 'standard' to apply TRaSH guide recommendations.
140+
'';
141+
};
142+
143+
quality_profiles = mkOption {
144+
type = types.listOf (types.submodule {
145+
options = {
146+
name = mkOption {
147+
type = types.str;
148+
description = "Name of the quality profile";
149+
example = "WEB-2160p";
150+
};
151+
152+
reset_unmatched_scores = mkOption {
153+
type = types.nullOr (types.submodule {
154+
options.enabled = mkOption {
155+
type = types.bool;
156+
default = true;
157+
description = "Whether to reset scores for custom formats not in the configuration";
158+
};
159+
});
160+
default = null;
161+
description = "Configuration for resetting unmatched custom format scores";
162+
};
163+
164+
upgrade = mkOption {
165+
type = types.nullOr (types.submodule {
166+
options = {
167+
allowed = mkOption {
168+
type = types.bool;
169+
default = true;
170+
description = "Whether upgrades are allowed";
171+
};
172+
173+
until_quality = mkOption {
174+
type = types.str;
175+
description = "Upgrade until this quality level is reached";
176+
example = "WEB 2160p";
177+
};
178+
179+
until_score = mkOption {
180+
type = types.int;
181+
default = 10000;
182+
description = "Upgrade until this custom format score is reached";
183+
};
184+
};
185+
});
186+
default = null;
187+
description = "Upgrade configuration for the quality profile";
188+
};
189+
190+
min_format_score = mkOption {
191+
type = types.int;
192+
default = 0;
193+
description = "Minimum custom format score required";
194+
};
195+
196+
quality_sort = mkOption {
197+
type = types.enum ["top" "bottom"];
198+
default = "top";
199+
description = "Quality sort order (top = highest quality first)";
200+
};
201+
202+
qualities = mkOption {
203+
type = types.listOf (types.submodule {
204+
options = {
205+
name = mkOption {
206+
type = types.str;
207+
description = "Quality name (e.g., 'WEB 2160p', 'Bluray-1080p')";
208+
};
209+
210+
qualities = mkOption {
211+
type = types.nullOr (types.listOf types.str);
212+
default = null;
213+
description = "Optional list of specific quality variants (e.g., ['WEBDL-2160p', 'WEBRip-2160p'])";
214+
};
215+
};
216+
});
217+
default = [];
218+
description = "Ordered list of qualities in the profile (from highest to lowest priority)";
219+
};
220+
};
221+
});
222+
default = [];
223+
description = "List of quality profiles to create or update";
224+
};
225+
226+
custom_formats = mkOption {
227+
type = types.listOf (types.submodule {
228+
options = {
229+
trash_ids = mkOption {
230+
type = types.listOf types.str;
231+
default = [];
232+
description = "List of TRaSH custom format IDs (GUIDs)";
233+
example = ["85c61753df5da1fb2aab6f2a47426b09"];
234+
};
235+
236+
assign_scores_to = mkOption {
237+
type = types.listOf (types.submodule {
238+
options.name = mkOption {
239+
type = types.str;
240+
description = "Name of the quality profile to assign scores to";
241+
};
242+
});
243+
default = [];
244+
description = "Quality profiles to assign these custom format scores to";
245+
};
246+
};
247+
});
248+
default = [];
249+
description = "List of custom format configurations from TRaSH guides";
250+
};
251+
252+
include = mkOption {
253+
type = types.listOf (types.submodule {
254+
options.template = mkOption {
255+
type = types.str;
256+
description = "Name of the TRaSH template to include";
257+
example = "sonarr-v4-quality-profile-web-2160p-alternative";
258+
};
259+
});
260+
default = [];
261+
description = "List of TRaSH guide templates to include";
262+
};
263+
};
264+
};
265+
in
266+
mkOption {
267+
type = types.nullOr (
268+
types.submodule {
269+
options = {
270+
sonarr = mkOption {
271+
type = types.nullOr (types.attrsOf instanceType);
272+
default = null;
273+
description = "Sonarr instance configurations";
274+
};
275+
276+
radarr = mkOption {
277+
type = types.nullOr (types.attrsOf instanceType);
278+
default = null;
279+
description = "Radarr instance configurations";
280+
};
281+
};
282+
}
283+
);
284+
default = null;
285+
defaultText = literalExpression "Generated from nixflix.recyclarr.sonarr and nixflix.recyclarr.radarr settings";
286+
example = literalExpression ''
287+
{
288+
sonarr.sonarr_main = {
289+
base_url = "http://127.0.0.1:8989";
290+
api_key._secret = "/run/credentials/recyclarr.service/sonarr-api_key";
291+
quality_definition.type = "series";
292+
quality_profiles = [{
293+
name = "WEB-2160p";
294+
upgrade = {
295+
allowed = true;
296+
until_quality = "WEB 2160p";
297+
until_score = 10000;
298+
};
299+
min_format_score = 0;
300+
quality_sort = "top";
301+
qualities = [
302+
{ name = "WEB 2160p"; qualities = ["WEBDL-2160p" "WEBRip-2160p"]; }
303+
{ name = "WEB 1080p"; qualities = ["WEBDL-1080p" "WEBRip-1080p"]; }
304+
];
305+
}];
306+
custom_formats = [{
307+
trash_ids = [
308+
"85c61753df5da1fb2aab6f2a47426b09" # BR-DISK
309+
"9c11cd3f07101cdba90a2d81cf0e56b4" # LQ
310+
];
311+
assign_scores_to = [{ name = "WEB-2160p"; }];
312+
}];
313+
include = [
314+
{ template = "sonarr-v4-quality-profile-web-2160p-alternative"; }
315+
];
316+
};
317+
radarr.radarr_main = {
318+
base_url = "http://127.0.0.1:7878";
319+
api_key._secret = "/run/credentials/recyclarr.service/radarr-api_key";
320+
quality_definition.type = "movie";
321+
};
322+
}
323+
'';
324+
description = ''
325+
Recyclarr configuration as a structured Nix attribute set.
326+
When set, this completely replaces the auto-generated configuration,
327+
giving you full control over the Recyclarr setup.
328+
329+
The structure is: { service_type.instance_name = { ... }; }
330+
- service_type: "sonarr" or "radarr" (only these two keys are allowed)
331+
- instance_name: arbitrary name for the instance (e.g., "sonarr_main", "radarr_4k")
332+
333+
Each instance supports quality profiles, custom formats from TRaSH guides,
334+
media naming configuration, and template includes.
335+
336+
https://recyclarr.dev/wiki/yaml/config-reference/
337+
'';
338+
}

0 commit comments

Comments
 (0)