mirror of
https://github.com/JaviPege/sailarr-installer.git
synced 2026-01-02 02:39:32 -06:00
**Problem:** JSON responses were still being printed to stdout when calling: - add_arr_to_prowlarr - add_root_folder - add_download_client - add_remote_path_mapping - delete_quality_profile This caused dozens of lines of JSON to clutter the installation output. **Root Cause:** api_call() does "echo $response" to return the response body. When functions call api_call but don't capture the output, it prints to stdout instead of being suppressed. **Solution:** Redirect stdout to /dev/null for all api_call invocations that: - Only check exit code (if api_call...) - Don't need the response body Functions that DO need the response (like get_quality_profiles) keep their output since they explicitly capture it. **Result:** Clean installation output showing only success/error messages. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
286 lines
9.0 KiB
Bash
Executable File
286 lines
9.0 KiB
Bash
Executable File
#!/bin/bash
|
|
# setup-api.sh - API interaction functions
|
|
# Library directory
|
|
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
# Handles all HTTP/API calls to services
|
|
|
|
source "${LIB_DIR}/setup-common.sh"
|
|
|
|
# Generic API call function
|
|
api_call() {
|
|
log_function_enter "api_call" "method=$1 service=$2 port=$3 endpoint=$4"
|
|
|
|
local method=$1 # GET, POST, PUT, DELETE
|
|
local service=$2 # Service name (radarr, sonarr, prowlarr)
|
|
local port=$3 # Service port
|
|
local endpoint=$4 # API endpoint (without /api/v3/)
|
|
local api_key=$5 # API key for authentication
|
|
local data=$6 # JSON data (optional)
|
|
local api_version=${7:-"v3"} # API version (default v3)
|
|
|
|
local url="http://localhost:${port}/api/${api_version}/${endpoint}"
|
|
local response
|
|
local http_code
|
|
|
|
# Only log the operation summary, not the full JSON payload
|
|
log_trace "api_call" "$method $url (${#data} bytes)"
|
|
|
|
# Build curl command
|
|
local curl_cmd="curl -s -w '\n%{http_code}' -X $method '$url' \
|
|
-H \"X-Api-Key: $api_key\" \
|
|
-H 'Content-Type: application/json'"
|
|
|
|
# Add data if provided
|
|
if [ -n "$data" ]; then
|
|
curl_cmd="$curl_cmd -d '$data'"
|
|
log_trace "api_call" "Request includes JSON payload"
|
|
fi
|
|
|
|
# Execute and capture response
|
|
log_trace "api_call" "Executing curl command"
|
|
response=$(eval $curl_cmd)
|
|
http_code=$(echo "$response" | tail -n1)
|
|
response=$(echo "$response" | sed '$d')
|
|
|
|
log_trace "api_call" "HTTP Status: $http_code, Response length: ${#response} bytes"
|
|
|
|
# Check HTTP status
|
|
if [[ "$http_code" =~ ^2 ]]; then
|
|
log_function_exit "api_call" 0 "HTTP $http_code"
|
|
echo "$response"
|
|
return 0
|
|
else
|
|
log_error "API call failed: $method $endpoint (HTTP $http_code)"
|
|
log_error "Response: $response"
|
|
log_function_exit "api_call" 1 "HTTP $http_code"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Extract API key from service config
|
|
extract_api_key() {
|
|
log_function_enter "extract_api_key" "$@"
|
|
|
|
local container_name=$1
|
|
local config_file=${2:-"/config/config.xml"}
|
|
local max_attempts=${3:-30}
|
|
local attempt=0
|
|
|
|
log_info "Extracting API key from $container_name..."
|
|
|
|
while [ $attempt -lt $max_attempts ]; do
|
|
local api_key=$(docker exec "$container_name" cat "$config_file" 2>/dev/null | grep -oP '(?<=<ApiKey>)[^<]+' 2>/dev/null)
|
|
|
|
if [ -n "$api_key" ] && [ "$api_key" != "0000000000000000000000000000000" ]; then
|
|
log_success "API key extracted from $container_name"
|
|
echo "$api_key"
|
|
return 0
|
|
fi
|
|
|
|
attempt=$((attempt + 1))
|
|
sleep 2
|
|
done
|
|
|
|
log_error "Failed to extract API key from $container_name after $max_attempts attempts"
|
|
return 1
|
|
}
|
|
|
|
# Add root folder to *arr service
|
|
add_root_folder() {
|
|
local service=$1 # radarr or sonarr
|
|
local port=$2
|
|
local api_key=$3
|
|
local path=$4 # /data/media/movies or /data/media/tv
|
|
local name=${5:-"Media"}
|
|
|
|
log_info "Adding root folder to $service: $path"
|
|
|
|
local data="{
|
|
\"path\": \"$path\",
|
|
\"name\": \"$name\"
|
|
}"
|
|
|
|
if api_call "POST" "$service" "$port" "rootfolder" "$api_key" "$data" > /dev/null; then
|
|
log_success "Root folder added to $service"
|
|
return 0
|
|
else
|
|
log_error "Failed to add root folder to $service"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Add download client to *arr service
|
|
add_download_client() {
|
|
local service=$1 # radarr or sonarr
|
|
local port=$2
|
|
local api_key=$3
|
|
local client_name=$4 # Decypharr or RDTClient
|
|
local client_host=$5 # decypharr or rdtclient
|
|
local client_port=$6
|
|
local client_api_key=$7
|
|
local category=$8 # movies, tv, radarr, or sonarr
|
|
|
|
log_info "Adding download client '$client_name' to $service"
|
|
|
|
# Determine category field names based on service
|
|
local category_fields=""
|
|
if [ "$service" = "radarr" ]; then
|
|
category_fields="{\"name\": \"movieCategory\", \"value\": \"$category\"},
|
|
{\"name\": \"recentMoviePriority\", \"value\": 0},
|
|
{\"name\": \"olderMoviePriority\", \"value\": 0},"
|
|
else
|
|
category_fields="{\"name\": \"tvCategory\", \"value\": \"$category\"},
|
|
{\"name\": \"recentTvPriority\", \"value\": 0},
|
|
{\"name\": \"olderTvPriority\", \"value\": 0},"
|
|
fi
|
|
|
|
local data="{
|
|
\"enable\": true,
|
|
\"protocol\": \"torrent\",
|
|
\"priority\": 1,
|
|
\"removeCompletedDownloads\": true,
|
|
\"removeFailedDownloads\": true,
|
|
\"name\": \"$client_name\",
|
|
\"fields\": [
|
|
{\"name\": \"host\", \"value\": \"$client_host\"},
|
|
{\"name\": \"port\", \"value\": $client_port},
|
|
{\"name\": \"useSsl\", \"value\": false},
|
|
{\"name\": \"urlBase\", \"value\": \"\"},
|
|
{\"name\": \"username\", \"value\": \"http://$service:$port\"},
|
|
{\"name\": \"password\", \"value\": \"$client_api_key\"},
|
|
$category_fields
|
|
{\"name\": \"initialState\", \"value\": 0},
|
|
{\"name\": \"sequentialOrder\", \"value\": false},
|
|
{\"name\": \"firstAndLast\", \"value\": false}
|
|
],
|
|
\"implementationName\": \"qBittorrent\",
|
|
\"implementation\": \"QBittorrent\",
|
|
\"configContract\": \"QBittorrentSettings\",
|
|
\"tags\": []
|
|
}"
|
|
|
|
if api_call "POST" "$service" "$port" "downloadclient" "$api_key" "$data" > /dev/null; then
|
|
log_success "Download client added to $service"
|
|
return 0
|
|
else
|
|
log_error "Failed to add download client to $service"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Add *arr application to Prowlarr
|
|
add_arr_to_prowlarr() {
|
|
log_function_enter "add_arr_to_prowlarr" "$1 $2 [api_key] $4 [prowlarr_key]"
|
|
|
|
local service=$1 # radarr or sonarr
|
|
local service_port=$2 # 7878 or 8989
|
|
local service_api_key=$3
|
|
local prowlarr_port=$4
|
|
local prowlarr_api_key=$5
|
|
|
|
log_info "Adding $service as application in Prowlarr"
|
|
|
|
# Determine sync categories based on service
|
|
local sync_categories=""
|
|
local contract_name=""
|
|
if [ "$service" = "radarr" ]; then
|
|
sync_categories="[2000,2010,2020,2030,2040,2045,2050,2060]"
|
|
contract_name="RadarrSettings"
|
|
else
|
|
sync_categories="[5000,5010,5020,5030,5040,5045,5050,5060,5070,5080,5090]"
|
|
contract_name="SonarrSettings"
|
|
fi
|
|
|
|
local data="{
|
|
\"name\": \"${service^}\",
|
|
\"syncLevel\": \"fullSync\",
|
|
\"implementation\": \"${service^}\",
|
|
\"implementationName\": \"${service^}\",
|
|
\"configContract\": \"$contract_name\",
|
|
\"fields\": [
|
|
{\"name\": \"prowlarrUrl\", \"value\": \"http://prowlarr:$prowlarr_port\"},
|
|
{\"name\": \"baseUrl\", \"value\": \"http://$service:$service_port\"},
|
|
{\"name\": \"apiKey\", \"value\": \"$service_api_key\"},
|
|
{\"name\": \"syncCategories\", \"value\": $sync_categories}
|
|
],
|
|
\"tags\": []
|
|
}"
|
|
|
|
if api_call "POST" "prowlarr" "$prowlarr_port" "applications" "$prowlarr_api_key" "$data" "v1" > /dev/null; then
|
|
log_success "${service^} application added to Prowlarr"
|
|
return 0
|
|
else
|
|
log_error "Failed to add ${service^} application to Prowlarr"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Add remote path mapping
|
|
add_remote_path_mapping() {
|
|
local service=$1
|
|
local port=$2
|
|
local api_key=$3
|
|
local remote_path=$4
|
|
local local_path=$5
|
|
local host=${6:-"decypharr"}
|
|
|
|
log_info "Adding remote path mapping to $service"
|
|
|
|
local data="{
|
|
\"host\": \"$host\",
|
|
\"remotePath\": \"$remote_path\",
|
|
\"localPath\": \"$local_path\"
|
|
}"
|
|
|
|
if api_call "POST" "$service" "$port" "remotepathmapping" "$api_key" "$data" > /dev/null; then
|
|
log_success "Remote path mapping added to $service"
|
|
return 0
|
|
else
|
|
log_warning "Failed to add remote path mapping to $service (may already exist)"
|
|
return 0 # Don't fail on this
|
|
fi
|
|
}
|
|
|
|
# Get quality profiles from service
|
|
get_quality_profiles() {
|
|
local service=$1
|
|
local port=$2
|
|
local api_key=$3
|
|
|
|
# Call API directly without logging to avoid stdout pollution
|
|
# (api_call logs contaminate the JSON output making it unparseable)
|
|
local response=$(curl -s -w '\n%{http_code}' -X GET "http://localhost:${port}/api/v3/qualityprofile" \
|
|
-H "X-Api-Key: $api_key" \
|
|
-H 'Content-Type: application/json')
|
|
|
|
local http_code=$(echo "$response" | tail -n1)
|
|
local body=$(echo "$response" | sed '$d')
|
|
|
|
if [[ "$http_code" =~ ^2 ]]; then
|
|
echo "$body"
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Delete quality profile
|
|
delete_quality_profile() {
|
|
local service=$1
|
|
local port=$2
|
|
local api_key=$3
|
|
local profile_id=$4
|
|
|
|
api_call "DELETE" "$service" "$port" "qualityprofile/$profile_id" "$api_key" > /dev/null
|
|
}
|
|
|
|
# Export functions
|
|
export -f api_call
|
|
export -f extract_api_key
|
|
export -f add_root_folder
|
|
export -f add_download_client
|
|
export -f add_arr_to_prowlarr
|
|
export -f add_remote_path_mapping
|
|
export -f get_quality_profiles
|
|
export -f delete_quality_profile
|