Fix try_smart_import() silently failing every ManualImport it fires

The candidate objects from /manualimport only nest ids under .series.id
and .movie.id, but Sonarr/Radarr's ManualImport command requires them
flattened to top-level seriesId/episodeIds or movieId — every call was
failing instantly with "Series/Movie with ID 0 does not exist" while the
caller only checked for HTTP 201 (accepted), so every "smart-imported"
this script has ever logged was actually a silent no-op. Now flattens
the ids before submitting and briefly polls the command afterward so a
fast failure falls through to the normal blocklist+research path instead
of being reported as success.
This commit is contained in:
Gmer4Lfe
2026-07-19 10:36:37 -04:00
parent 54919db13f
commit 7252d4aad3
+33 -4
View File
@@ -378,6 +378,14 @@ try_smart_import() {
existing_res=$(echo "$existing" | jq -r '.episodeFile.quality.quality.resolution // 0' 2>/dev/null)
existing_lang=$(echo "$existing" | jq -r '.episodeFile.languages[0].name // "Unknown"' 2>/dev/null)
fi
# /manualimport only nests the IDs under .series.id / .episodes[].id —
# the ManualImport command body needs them flattened to top-level
# seriesId/episodeIds or Sonarr rejects the whole command with
# "Series with ID 0 does not exist" (confirmed live 2026-07-19: every
# smart-import this run reported as successful had actually failed
# this way, silently, since the caller only checks the HTTP 201 accept).
entry=$(echo "$entry" | jq -c --argjson eid "$target_id" \
'. + {seriesId: .series.id, episodeIds: [$eid]}' 2>/dev/null)
;;
radarr)
target_id=$(echo "$entry" | jq -r '.movie.id // empty' 2>/dev/null)
@@ -391,10 +399,14 @@ try_smart_import() {
existing_res=$(echo "$existing" | jq -r '.quality.quality.resolution // 0' 2>/dev/null)
existing_lang=$(echo "$existing" | jq -r '.languages[0].name // "Unknown"' 2>/dev/null)
fi
# Same flattening issue as Sonarr above — command body needs a
# top-level movieId or Radarr rejects it with "Movie with ID 0
# does not exist".
entry=$(echo "$entry" | jq -c --argjson mid "$target_id" '. + {movieId: $mid}' 2>/dev/null)
;;
esac
[[ -z "$target_id" ]] && return 1
[[ -z "$target_id" || -z "$entry" ]] && return 1
if [[ "$has_file" != "true" ]]; then
decision="import" # nothing there yet — fills a real gap
@@ -419,19 +431,36 @@ try_smart_import() {
return 0
fi
local files_json cmd_body http_code
local files_json cmd_body response http_code cmd_id cmd_status
files_json=$(printf '%s\n' "${qualifying_files[@]}" | jq -s -c '.' 2>/dev/null)
[[ -z "$files_json" ]] && return 1
cmd_body=$(jq -c -n --argjson files "$files_json" \
'{name:"ManualImport", files:$files, importMode:"auto"}' 2>/dev/null)
[[ -z "$cmd_body" ]] && return 1
http_code=$(curl -s -o /dev/null -w '%{http_code}' -X POST \
response=$(curl -s -w '\n%{http_code}' -X POST \
-H "X-Api-Key: $api_key" -H "Content-Type: application/json" \
-d "$cmd_body" \
"${url}/api/${api_version}/command" 2>/dev/null)
http_code=$(echo "$response" | tail -1)
cmd_id=$(echo "$response" | head -n -1 | jq -r '.id // empty' 2>/dev/null)
[[ "$http_code" == "201" ]]
[[ "$http_code" != "201" || -z "$cmd_id" ]] && return 1
# A bad payload (e.g. the seriesId/movieId=0 bug this was written to catch)
# fails in ~10ms — well before any real file copy would even start — so a
# brief poll here catches that failure class without racing a genuinely
# long-running import, which is the reason this doesn't poll to completion.
local _i
for _i in 1 2 3; do
sleep 1
cmd_status=$(curl -sf --max-time 10 -H "X-Api-Key: $api_key" \
"${url}/api/${api_version}/command/${cmd_id}" 2>/dev/null | jq -r '.status // empty')
[[ "$cmd_status" == "failed" ]] && return 1
[[ "$cmd_status" == "completed" ]] && break
done
return 0
}
# ==============================================================================================