cleanup category handling
This commit is contained in:
49
module.nix
49
module.nix
@@ -112,7 +112,13 @@ let
|
|||||||
syncCategories = lib.mkOption {
|
syncCategories = lib.mkOption {
|
||||||
type = lib.types.listOf lib.types.int;
|
type = lib.types.listOf lib.types.int;
|
||||||
default = [ ];
|
default = [ ];
|
||||||
description = "List of sync category IDs for the application.";
|
description = ''
|
||||||
|
List of Newznab category IDs to sync for this application.
|
||||||
|
When empty (default), categories are auto-detected at runtime
|
||||||
|
by querying Prowlarr's indexer/categories API endpoint and
|
||||||
|
collecting all IDs under the parent category matching the
|
||||||
|
implementation type (e.g. Sonarr -> TV, Radarr -> Movies).
|
||||||
|
'';
|
||||||
example = [
|
example = [
|
||||||
5000
|
5000
|
||||||
5010
|
5010
|
||||||
@@ -291,6 +297,44 @@ let
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Map Servarr implementation names to their Newznab parent category names.
|
||||||
|
# Used to auto-detect syncCategories from the Prowlarr API when not explicitly set.
|
||||||
|
implementationCategoryMap = {
|
||||||
|
Sonarr = "TV";
|
||||||
|
Radarr = "Movies";
|
||||||
|
Lidarr = "Audio";
|
||||||
|
Readarr = "Books";
|
||||||
|
Whisparr = "XXX";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Emit shell code that sets SYNC_CATEGORIES to a JSON array of category IDs.
|
||||||
|
# When the user provides explicit IDs, use those. Otherwise, query the Prowlarr
|
||||||
|
# /indexer/categories endpoint and collect the parent + all subcategory IDs for
|
||||||
|
# the implementation's Newznab category.
|
||||||
|
mkResolveSyncCategories =
|
||||||
|
app:
|
||||||
|
let
|
||||||
|
hasExplicit = app.syncCategories != [ ];
|
||||||
|
categoryName = implementationCategoryMap.${app.implementation} or null;
|
||||||
|
in
|
||||||
|
if hasExplicit then
|
||||||
|
"SYNC_CATEGORIES=${lib.escapeShellArg (builtins.toJSON app.syncCategories)}"
|
||||||
|
else if categoryName != null then
|
||||||
|
''
|
||||||
|
echo "Auto-detecting sync categories for ${app.implementation}..."
|
||||||
|
ALL_CATEGORIES=$(${curl} -sf "$BASE_URL/indexer/categories" -H "X-Api-Key: $API_KEY")
|
||||||
|
SYNC_CATEGORIES=$(echo "$ALL_CATEGORIES" | ${jq} --arg name ${lib.escapeShellArg categoryName} \
|
||||||
|
'[.[] | select(.name == $name) | .id, .subCategories[].id]')
|
||||||
|
if [ "$SYNC_CATEGORIES" = "[]" ] || [ -z "$SYNC_CATEGORIES" ]; then
|
||||||
|
echo "Warning: could not auto-detect categories for '${categoryName}', using empty list" >&2
|
||||||
|
SYNC_CATEGORIES='[]'
|
||||||
|
else
|
||||||
|
echo "Resolved sync categories: $SYNC_CATEGORIES"
|
||||||
|
fi
|
||||||
|
''
|
||||||
|
else
|
||||||
|
"SYNC_CATEGORIES='[]' ";
|
||||||
|
|
||||||
curl = "${pkgs.curl}/bin/curl";
|
curl = "${pkgs.curl}/bin/curl";
|
||||||
jq = "${pkgs.jq}/bin/jq";
|
jq = "${pkgs.jq}/bin/jq";
|
||||||
grep = "${pkgs.gnugrep}/bin/grep";
|
grep = "${pkgs.gnugrep}/bin/grep";
|
||||||
@@ -353,6 +397,7 @@ let
|
|||||||
echo "Synced app '${app.name}' already exists, skipping"
|
echo "Synced app '${app.name}' already exists, skipping"
|
||||||
else
|
else
|
||||||
echo "Adding synced app '${app.name}'..."
|
echo "Adding synced app '${app.name}'..."
|
||||||
|
${mkResolveSyncCategories app}
|
||||||
PAYLOAD=$(${jq} -n \
|
PAYLOAD=$(${jq} -n \
|
||||||
--arg name ${lib.escapeShellArg app.name} \
|
--arg name ${lib.escapeShellArg app.name} \
|
||||||
--arg implementation ${lib.escapeShellArg app.implementation} \
|
--arg implementation ${lib.escapeShellArg app.implementation} \
|
||||||
@@ -361,7 +406,7 @@ let
|
|||||||
--arg prowlarrUrl ${lib.escapeShellArg app.prowlarrUrl} \
|
--arg prowlarrUrl ${lib.escapeShellArg app.prowlarrUrl} \
|
||||||
--arg baseUrl ${lib.escapeShellArg app.baseUrl} \
|
--arg baseUrl ${lib.escapeShellArg app.baseUrl} \
|
||||||
--arg apiKey "$TARGET_API_KEY" \
|
--arg apiKey "$TARGET_API_KEY" \
|
||||||
--argjson syncCategories ${builtins.toJSON app.syncCategories} \
|
--argjson syncCategories "$SYNC_CATEGORIES" \
|
||||||
'{
|
'{
|
||||||
name: $name,
|
name: $name,
|
||||||
implementation: $implementation,
|
implementation: $implementation,
|
||||||
|
|||||||
@@ -182,16 +182,7 @@ pkgs.testers.runNixOSTest {
|
|||||||
prowlarrUrl = "http://localhost:9696";
|
prowlarrUrl = "http://localhost:9696";
|
||||||
baseUrl = "http://localhost:8989";
|
baseUrl = "http://localhost:8989";
|
||||||
apiKeyFrom = "/var/lib/sonarr/.config/NzbDrone/config.xml";
|
apiKeyFrom = "/var/lib/sonarr/.config/NzbDrone/config.xml";
|
||||||
syncCategories = [
|
# syncCategories omitted — auto-detected from Prowlarr API
|
||||||
5000
|
|
||||||
5010
|
|
||||||
5020
|
|
||||||
5030
|
|
||||||
5040
|
|
||||||
5045
|
|
||||||
5050
|
|
||||||
5090
|
|
||||||
];
|
|
||||||
serviceName = "sonarr";
|
serviceName = "sonarr";
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -201,18 +192,7 @@ pkgs.testers.runNixOSTest {
|
|||||||
prowlarrUrl = "http://localhost:9696";
|
prowlarrUrl = "http://localhost:9696";
|
||||||
baseUrl = "http://localhost:7878";
|
baseUrl = "http://localhost:7878";
|
||||||
apiKeyFrom = "/var/lib/radarr/.config/Radarr/config.xml";
|
apiKeyFrom = "/var/lib/radarr/.config/Radarr/config.xml";
|
||||||
syncCategories = [
|
# syncCategories omitted — auto-detected from Prowlarr API
|
||||||
2000
|
|
||||||
2010
|
|
||||||
2020
|
|
||||||
2030
|
|
||||||
2040
|
|
||||||
2045
|
|
||||||
2050
|
|
||||||
2060
|
|
||||||
2070
|
|
||||||
2080
|
|
||||||
];
|
|
||||||
serviceName = "radarr";
|
serviceName = "radarr";
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -325,6 +305,34 @@ pkgs.testers.runNixOSTest {
|
|||||||
"jq -e '.[] | select(.name == \"Radarr\")'"
|
"jq -e '.[] | select(.name == \"Radarr\")'"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Verify auto-detected syncCategories are non-empty for Sonarr (TV: 5xxx)
|
||||||
|
machine.succeed(
|
||||||
|
"API_KEY=$(grep -oP '(?<=<ApiKey>)[^<]+' /var/lib/prowlarr/config.xml) && "
|
||||||
|
"curl -sf http://localhost:9696/api/v1/applications -H \"X-Api-Key: $API_KEY\" | "
|
||||||
|
"jq -e '.[] | select(.name == \"Sonarr\") | .fields[] | select(.name == \"syncCategories\") | .value | length > 0'"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify auto-detected syncCategories contain parent TV category (5000)
|
||||||
|
machine.succeed(
|
||||||
|
"API_KEY=$(grep -oP '(?<=<ApiKey>)[^<]+' /var/lib/prowlarr/config.xml) && "
|
||||||
|
"curl -sf http://localhost:9696/api/v1/applications -H \"X-Api-Key: $API_KEY\" | "
|
||||||
|
"jq -e '.[] | select(.name == \"Sonarr\") | .fields[] | select(.name == \"syncCategories\") | .value | map(select(. == 5000)) | length > 0'"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify auto-detected syncCategories are non-empty for Radarr (Movies: 2xxx)
|
||||||
|
machine.succeed(
|
||||||
|
"API_KEY=$(grep -oP '(?<=<ApiKey>)[^<]+' /var/lib/prowlarr/config.xml) && "
|
||||||
|
"curl -sf http://localhost:9696/api/v1/applications -H \"X-Api-Key: $API_KEY\" | "
|
||||||
|
"jq -e '.[] | select(.name == \"Radarr\") | .fields[] | select(.name == \"syncCategories\") | .value | length > 0'"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify auto-detected syncCategories contain parent Movies category (2000)
|
||||||
|
machine.succeed(
|
||||||
|
"API_KEY=$(grep -oP '(?<=<ApiKey>)[^<]+' /var/lib/prowlarr/config.xml) && "
|
||||||
|
"curl -sf http://localhost:9696/api/v1/applications -H \"X-Api-Key: $API_KEY\" | "
|
||||||
|
"jq -e '.[] | select(.name == \"Radarr\") | .fields[] | select(.name == \"syncCategories\") | .value | map(select(. == 2000)) | length > 0'"
|
||||||
|
)
|
||||||
|
|
||||||
# Idempotency test: restart init services and verify no duplicate entries
|
# Idempotency test: restart init services and verify no duplicate entries
|
||||||
machine.succeed("systemctl restart sonarr-init.service")
|
machine.succeed("systemctl restart sonarr-init.service")
|
||||||
machine.succeed("systemctl restart radarr-init.service")
|
machine.succeed("systemctl restart radarr-init.service")
|
||||||
|
|||||||
Reference in New Issue
Block a user