|
5 | 5 | # --------------------------- |
6 | 6 | # Use environment variables if provided; otherwise, fall back to defaults. |
7 | 7 | 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"} |
9 | 9 |
|
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} |
12 | 12 |
|
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} |
15 | 16 |
|
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 |
20 | 18 | RANDOM_SELECTION=${RANDOM_SELECTION:-true} |
21 | 19 |
|
22 | 20 | # --------------------------- |
23 | 21 | # Main infinite loop |
24 | 22 | # --------------------------- |
25 | 23 | 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 \ |
30 | 27 | -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") |
34 | 29 |
|
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..." |
37 | 33 | sleep 60 |
38 | 34 | continue |
39 | 35 | fi |
40 | 36 |
|
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..." |
44 | 44 | sleep 60 |
45 | 45 | continue |
46 | 46 | fi |
| 47 | + echo "Found $TOTAL_INCOMPLETE show(s) with missing episodes." |
47 | 48 |
|
48 | | - echo "Found $TOTAL_MISSING missing movie(s)." |
49 | 49 | 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." |
51 | 51 |
|
52 | | - MOVIES_PROCESSED=0 |
| 52 | + SHOWS_PROCESSED=0 |
53 | 53 | ALREADY_CHECKED=() |
54 | 54 |
|
55 | 55 | 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..." |
59 | 59 | break |
60 | 60 | fi |
61 | 61 |
|
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..." |
65 | 65 | sleep 60 |
66 | 66 | break |
67 | 67 | fi |
68 | 68 |
|
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 |
71 | 72 | while true; do |
72 | | - INDEX=$((RANDOM % TOTAL_MISSING)) |
| 73 | + INDEX=$((RANDOM % TOTAL_INCOMPLETE)) |
73 | 74 | if [[ ! " ${ALREADY_CHECKED[*]} " =~ " ${INDEX} " ]]; then |
74 | 75 | break |
75 | 76 | fi |
76 | 77 | done |
77 | 78 | else |
78 | | - for ((i=0; i<TOTAL_MISSING; i++)); do |
| 79 | + for ((i=0; i<TOTAL_INCOMPLETE; i++)); do |
79 | 80 | if [[ ! " ${ALREADY_CHECKED[*]} " =~ " ${i} " ]]; then |
80 | 81 | INDEX=$i |
81 | 82 | break |
82 | 83 | fi |
83 | 84 | done |
84 | 85 | fi |
85 | 86 |
|
| 87 | + # Add this index to the list of checked indices |
86 | 88 | ALREADY_CHECKED+=("$INDEX") |
87 | 89 |
|
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 | + |
98 | 105 | REFRESH_COMMAND=$(curl -s -X POST \ |
99 | 106 | -H "X-Api-Key: $API_KEY" \ |
100 | 107 | -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 |
105 | 112 | 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:" |
108 | 176 | 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 |
110 | 180 | 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" |
162 | 181 | fi |
163 | 182 | done |
164 | 183 |
|
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..." |
169 | 189 | sleep 60 |
170 | 190 | fi |
171 | 191 | done |
0 commit comments