Skip to content

Commit 8a6bc5b

Browse files
authored
Update sonarr-hunter.sh
1 parent 8533489 commit 8a6bc5b

File tree

1 file changed

+126
-106
lines changed

1 file changed

+126
-106
lines changed

sonarr-hunter.sh

Lines changed: 126 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -5,167 +5,187 @@
55
# ---------------------------
66
# Use environment variables if provided; otherwise, fall back to defaults.
77
API_KEY=${API_KEY:-"your-api-key"}
8-
API_URL=${API_URL:-"http://your-radarr-address:7878"}
8+
API_URL=${API_URL:-"http://your-address:8989"}
99

10-
# How many movies to process before restarting the search cycle
11-
MAX_MOVIES=${MAX_MOVIES:-1}
10+
# How many shows to process before restarting the search cycle
11+
MAX_SHOWS=${MAX_SHOWS:-1}
1212

13-
# Sleep duration in seconds after fully processing a movie (300=5min)
14-
SLEEP_DURATION=${SLEEP_DURATION:-300}
13+
# Sleep duration in seconds after finding a show with missing episodes
14+
# (900=15min, 600=10min, etc.)
15+
SLEEP_DURATION=${SLEEP_DURATION:-900}
1516

16-
# Shorter sleep duration between each processed movie (30=0.5min)
17-
REFRESH_DURATION=${REFRESH_DURATION:-30}
18-
19-
# Set to true to pick movies randomly, false to go in order
17+
# Set to true to pick shows randomly, false to go in order
2018
RANDOM_SELECTION=${RANDOM_SELECTION:-true}
2119

2220
# ---------------------------
2321
# Main infinite loop
2422
# ---------------------------
2523
while true; do
26-
echo "Retrieving missing movies from Radarr..."
27-
28-
# Fetch all monitored movies that do not have files
29-
MISSING_MOVIES_JSON=$(curl -s \
24+
echo "Retrieving missing episodes data from Sonarr..."
25+
# Get all shows first
26+
SHOWS_JSON=$(curl -s \
3027
-H "X-Api-Key: $API_KEY" \
31-
"$API_URL/api/v3/movie" | \
32-
jq '[.[] | select(.monitored == true and .hasFile == false)]'
33-
)
28+
"$API_URL/api/v3/series")
3429

35-
if [ -z "$MISSING_MOVIES_JSON" ]; then
36-
echo "ERROR: Unable to retrieve movie data from Radarr. Retrying in 60 seconds..."
30+
# If the above command fails or returns nothing, wait and retry
31+
if [ -z "$SHOWS_JSON" ]; then
32+
echo "ERROR: Unable to retrieve series data from Sonarr. Retrying in 60 seconds..."
3733
sleep 60
3834
continue
3935
fi
4036

41-
TOTAL_MISSING=$(echo "$MISSING_MOVIES_JSON" | jq 'length')
42-
if [ "$TOTAL_MISSING" -eq 0 ]; then
43-
echo "No missing movies found. Waiting 60 seconds before checking again..."
37+
# Filter to only get shows with missing episodes
38+
INCOMPLETE_SHOWS_JSON=$(echo "$SHOWS_JSON" | jq '[.[] | select(has("statistics") and .statistics.episodeCount > .statistics.episodeFileCount)]')
39+
40+
# Count how many incomplete shows are in the list
41+
TOTAL_INCOMPLETE=$(echo "$INCOMPLETE_SHOWS_JSON" | jq 'length')
42+
if [ "$TOTAL_INCOMPLETE" -eq 0 ]; then
43+
echo "No shows with missing episodes found in Sonarr. Waiting 60 seconds before checking again..."
4444
sleep 60
4545
continue
4646
fi
47+
echo "Found $TOTAL_INCOMPLETE show(s) with missing episodes."
4748

48-
echo "Found $TOTAL_MISSING missing movie(s)."
4949
echo "Using ${RANDOM_SELECTION:+random}${RANDOM_SELECTION:-sequential} selection."
50-
echo "Will process up to $MAX_MOVIES movies with a longer sleep of $SLEEP_DURATION seconds after the final one."
50+
echo "Will process up to ${MAX_SHOWS:-all} shows with ${SLEEP_DURATION}s pause between each."
5151

52-
MOVIES_PROCESSED=0
52+
SHOWS_PROCESSED=0
5353
ALREADY_CHECKED=()
5454

5555
while true; do
56-
# Check if we've reached the maximum number of movies to process
57-
if [ "$MAX_MOVIES" -gt 0 ] && [ "$MOVIES_PROCESSED" -ge "$MAX_MOVIES" ]; then
58-
echo "Reached maximum number of movies ($MAX_MOVIES). Restarting search cycle..."
56+
# Check if we've reached the maximum number of shows to process
57+
if [ "$MAX_SHOWS" -gt 0 ] && [ "$SHOWS_PROCESSED" -ge "$MAX_SHOWS" ]; then
58+
echo "Reached maximum number of shows to process ($MAX_SHOWS). Restarting search cycle..."
5959
break
6060
fi
6161

62-
# Check if we've checked all missing movies
63-
if [ ${#ALREADY_CHECKED[@]} -eq "$TOTAL_MISSING" ]; then
64-
echo "All missing movies have been checked. Waiting before starting a new cycle..."
62+
# Check if we've checked all incomplete shows
63+
if [ ${#ALREADY_CHECKED[@]} -eq "$TOTAL_INCOMPLETE" ] || [ "$TOTAL_INCOMPLETE" -eq 0 ]; then
64+
echo "All shows with missing episodes have been checked. Waiting before starting a new cycle..."
6565
sleep 60
6666
break
6767
fi
6868

69-
# Select next movie index based on selection method
70-
if [ "$RANDOM_SELECTION" = true ] && [ "$TOTAL_MISSING" -gt 1 ]; then
69+
# Select next show index based on selection method
70+
if [ "$RANDOM_SELECTION" = true ] && [ "$TOTAL_INCOMPLETE" -gt 1 ]; then
71+
# Keep generating random indices until we find one we haven't checked yet
7172
while true; do
72-
INDEX=$((RANDOM % TOTAL_MISSING))
73+
INDEX=$((RANDOM % TOTAL_INCOMPLETE))
7374
if [[ ! " ${ALREADY_CHECKED[*]} " =~ " ${INDEX} " ]]; then
7475
break
7576
fi
7677
done
7778
else
78-
for ((i=0; i<TOTAL_MISSING; i++)); do
79+
for ((i=0; i<TOTAL_INCOMPLETE; i++)); do
7980
if [[ ! " ${ALREADY_CHECKED[*]} " =~ " ${i} " ]]; then
8081
INDEX=$i
8182
break
8283
fi
8384
done
8485
fi
8586

87+
# Add this index to the list of checked indices
8688
ALREADY_CHECKED+=("$INDEX")
8789

88-
# Extract movie information
89-
MOVIE=$(echo "$MISSING_MOVIES_JSON" | jq ".[$INDEX]")
90-
MOVIE_ID=$(echo "$MOVIE" | jq '.id')
91-
MOVIE_TITLE=$(echo "$MOVIE" | jq -r '.title')
92-
MOVIE_YEAR=$(echo "$MOVIE" | jq -r '.year')
93-
94-
echo "Selected missing movie \"$MOVIE_TITLE ($MOVIE_YEAR)\"..."
95-
96-
# 1. Refresh the movie
97-
echo "1. Refreshing movie information for \"$MOVIE_TITLE\"..."
90+
# Extract show information
91+
SHOW=$(echo "$INCOMPLETE_SHOWS_JSON" | jq ".[$INDEX]")
92+
SHOW_ID=$(echo "$SHOW" | jq '.id')
93+
SHOW_TITLE=$(echo "$SHOW" | jq -r '.title')
94+
EPISODE_COUNT=$(echo "$SHOW" | jq '.statistics.episodeCount')
95+
EPISODE_FILE_COUNT=$(echo "$SHOW" | jq '.statistics.episodeFileCount')
96+
MISSING=$((EPISODE_COUNT - EPISODE_FILE_COUNT))
97+
98+
echo "Selected show \"$SHOW_TITLE\" with $MISSING missing episode(s)..."
99+
100+
# ---------------------------
101+
# Step 1: Refresh the series to make sure Sonarr has latest information
102+
# ---------------------------
103+
echo "1. Refreshing series information for \"$SHOW_TITLE\" (ID: $SHOW_ID)..."
104+
98105
REFRESH_COMMAND=$(curl -s -X POST \
99106
-H "X-Api-Key: $API_KEY" \
100107
-H "Content-Type: application/json" \
101-
-d "{\"name\":\"RefreshMovie\",\"movieIds\":[$MOVIE_ID]}" \
102-
"$API_URL/api/v3/command"
103-
)
104-
108+
-d "{\"name\":\"RefreshSeries\",\"seriesId\":$SHOW_ID}" \
109+
"$API_URL/api/v3/command")
110+
111+
# Check if the refresh command succeeded
105112
REFRESH_ID=$(echo "$REFRESH_COMMAND" | jq '.id // empty')
106-
if [ -z "$REFRESH_ID" ]; then
107-
echo "WARNING: Could not refresh \"$MOVIE_TITLE\". Response was:"
113+
if [ -n "$REFRESH_ID" ]; then
114+
echo "Refresh command accepted (ID: $REFRESH_ID)."
115+
116+
# Wait for the refresh to complete
117+
echo "Waiting for refresh to complete..."
118+
sleep 5
119+
120+
# ---------------------------
121+
# Step 2: Tell Sonarr to search for missing episodes
122+
# ---------------------------
123+
echo "2. Telling Sonarr to perform a missing-episode search for \"$SHOW_TITLE\" (ID: $SHOW_ID)..."
124+
125+
SEARCH_COMMAND=$(curl -s -X POST \
126+
-H "X-Api-Key: $API_KEY" \
127+
-H "Content-Type: application/json" \
128+
-d "{\"name\":\"MissingEpisodeSearch\",\"seriesId\":$SHOW_ID}" \
129+
"$API_URL/api/v3/command")
130+
131+
# Verify the response from Sonarr
132+
SEARCH_ID=$(echo "$SEARCH_COMMAND" | jq '.id // empty')
133+
if [ -n "$SEARCH_ID" ]; then
134+
echo "Search command accepted (ID: $SEARCH_ID)."
135+
136+
# Wait a moment to let Sonarr process the command
137+
echo "Waiting 2 seconds to check command status..."
138+
sleep 2
139+
140+
# Check the status of the command
141+
COMMAND_STATUS=$(curl -s \
142+
-H "X-Api-Key: $API_KEY" \
143+
"$API_URL/api/v3/command/$SEARCH_ID" | jq -r '.status')
144+
145+
echo "Command status: $COMMAND_STATUS"
146+
SHOWS_PROCESSED=$((SHOWS_PROCESSED + 1))
147+
148+
# Sleep after processing a show with missing episodes
149+
echo "Show with missing episodes processed. Sleeping for $SLEEP_DURATION seconds to avoid overloading indexers..."
150+
sleep "$SLEEP_DURATION"
151+
else
152+
echo "WARNING: Search command did not return an ID. Response was:"
153+
echo "$SEARCH_COMMAND"
154+
echo "Trying alternative commands..."
155+
156+
# Try an alternative command for Sonarr v3
157+
SEARCH_COMMAND2=$(curl -s -X POST \
158+
-H "X-Api-Key: $API_KEY" \
159+
-H "Content-Type: application/json" \
160+
-d "{\"name\":\"EpisodeSearch\",\"seriesId\":$SHOW_ID}" \
161+
"$API_URL/api/v3/command")
162+
163+
SEARCH_ID2=$(echo "$SEARCH_COMMAND2" | jq '.id // empty')
164+
if [ -n "$SEARCH_ID2" ]; then
165+
echo "Alternative search command accepted (ID: $SEARCH_ID2)."
166+
SHOWS_PROCESSED=$((SHOWS_PROCESSED + 1))
167+
echo "Show with missing episodes processed. Sleeping for $SLEEP_DURATION seconds to avoid overloading indexers..."
168+
sleep "$SLEEP_DURATION"
169+
else
170+
echo "All search commands failed. Skipping this show."
171+
sleep 10
172+
fi
173+
fi
174+
else
175+
echo "WARNING: Refresh command did not return an ID. Response was:"
108176
echo "$REFRESH_COMMAND"
109-
echo "Skipping this movie. Sleeping 10 seconds..."
177+
echo "Skipping search for this show."
178+
179+
# Sleep a shorter time since we didn't actually do a search
110180
sleep 10
111-
continue
112-
fi
113-
114-
echo "Refresh command accepted (ID: $REFRESH_ID). Waiting 5 seconds for refresh to complete..."
115-
sleep 5
116-
117-
# 2. Search for the movie using "MoviesSearch"
118-
echo "2. Searching for \"$MOVIE_TITLE\"..."
119-
SEARCH_COMMAND=$(curl -s -X POST \
120-
-H "X-Api-Key: $API_KEY" \
121-
-H "Content-Type: application/json" \
122-
-d "{\"name\":\"MoviesSearch\",\"movieIds\":[$MOVIE_ID]}" \
123-
"$API_URL/api/v3/command"
124-
)
125-
126-
SEARCH_ID=$(echo "$SEARCH_COMMAND" | jq '.id // empty')
127-
if [ -n "$SEARCH_ID" ]; then
128-
echo "Search command accepted (ID: $SEARCH_ID)."
129-
else
130-
echo "WARNING: Search command failed for \"$MOVIE_TITLE\". Response was:"
131-
echo "$SEARCH_COMMAND"
132-
fi
133-
134-
echo "Waiting 5 seconds for search operation..."
135-
sleep 5
136-
137-
# 3. Rescan the movie folder
138-
echo "3. Rescanning movie folder for \"$MOVIE_TITLE\"..."
139-
RESCAN_COMMAND=$(curl -s -X POST \
140-
-H "X-Api-Key: $API_KEY" \
141-
-H "Content-Type: application/json" \
142-
-d "{\"name\":\"RescanMovie\",\"movieIds\":[$MOVIE_ID]}" \
143-
"$API_URL/api/v3/command"
144-
)
145-
146-
RESCAN_ID=$(echo "$RESCAN_COMMAND" | jq '.id // empty')
147-
if [ -n "$RESCAN_ID" ]; then
148-
echo "Rescan command accepted (ID: $RESCAN_ID)."
149-
else
150-
echo "WARNING: Rescan command not available or failed."
151-
fi
152-
153-
MOVIES_PROCESSED=$((MOVIES_PROCESSED + 1))
154-
155-
# Sleep before processing the next movie
156-
if [ "$MOVIES_PROCESSED" -ge "$MAX_MOVIES" ]; then
157-
echo "Processed $MOVIES_PROCESSED movies. Sleeping for $SLEEP_DURATION seconds..."
158-
sleep "$SLEEP_DURATION"
159-
else
160-
echo "Movie refreshed. Sleeping for $REFRESH_DURATION seconds before continuing..."
161-
sleep "$REFRESH_DURATION"
162181
fi
163182
done
164183

165-
echo "Done. Processed $MOVIES_PROCESSED missing movies in this cycle."
166-
167-
if [ "$MOVIES_PROCESSED" -eq 0 ]; then
168-
echo "No missing movies processed this cycle. Waiting 60 seconds before starting a new cycle..."
184+
echo "Done. Processed $SHOWS_PROCESSED shows with missing episodes in this cycle."
185+
186+
# If we didn't find any shows to process in this cycle, wait a bit before starting a new cycle
187+
if [ "$SHOWS_PROCESSED" -eq 0 ]; then
188+
echo "No shows with missing episodes processed this cycle. Waiting 60 seconds before starting a new cycle..."
169189
sleep 60
170190
fi
171191
done

0 commit comments

Comments
 (0)