-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathradarrswitchroot.sh
More file actions
executable file
Β·335 lines (273 loc) Β· 11.2 KB
/
Copy pathradarrswitchroot.sh
File metadata and controls
executable file
Β·335 lines (273 loc) Β· 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
#!/bin/bash
# Radarr Movie Manager Script - Interactive TUI
# Usage: ./radarrswitchroot.sh
# Configuration file path
CONFIG_FILE=".radarrswitchroot.conf"
# Check required dependencies
for cmd in fzf jq curl; do
if ! command -v "$cmd" &> /dev/null; then
echo "Error: $cmd is not installed. Please install it first:"
echo " macOS: brew install $cmd"
echo " Linux: sudo apt install $cmd"
exit 1
fi
done
# Function to setup configuration
setup_config() {
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββ"
echo "π§ First Time Setup"
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββ"
echo ""
# Prompt for Radarr URL
read -p "Enter Radarr URL (e.g., https://radarr.example.com): " RADARR_URL
RADARR_URL=$(echo "$RADARR_URL" | xargs) # Trim whitespace
# Prompt for API Key
read -p "Enter Radarr API Key: " API_KEY
API_KEY=$(echo "$API_KEY" | xargs) # Trim whitespace
echo ""
echo "π‘ Fetching root folders from Radarr..."
# Fetch root folders from Radarr
ROOT_FOLDERS=$(curl -s -X GET "$RADARR_URL/api/v3/rootfolder" \
-H "X-Api-Key: $API_KEY")
if [ -z "$ROOT_FOLDERS" ] || [ "$ROOT_FOLDERS" == "[]" ]; then
echo "β Error: Failed to fetch root folders. Please check your URL and API key."
exit 1
fi
# Format root folders for fzf
FOLDER_LIST=$(echo "$ROOT_FOLDERS" | jq -r '.[] | "\(.id)|\(.path)|\(.freeSpace // 0)"' | \
awk -F'|' '{
freeSpace = $3 / 1073741824 # Convert to GB
printf "%3d β %-60s β %.1f GB free\n", $1, $2, freeSpace
}')
if [ -z "$FOLDER_LIST" ]; then
echo "β Error: No root folders found"
exit 1
fi
echo ""
echo "Select target root folder:"
echo ""
# Use fzf to select root folder
SELECTED_FOLDER=$(echo "$FOLDER_LIST" | fzf \
--height=60% \
--border \
--header="ID β Path β Free Space" \
--header-first \
--prompt="π Select root folder: " \
--pointer="βΆ")
if [ -z "$SELECTED_FOLDER" ]; then
echo "β Setup cancelled"
exit 0
fi
# Extract folder path from selection
NEW_ROOT_FOLDER=$(echo "$SELECTED_FOLDER" | awk -F'β' '{print $2}' | xargs)
# Save configuration
cat > "$CONFIG_FILE" <<EOF
RADARR_URL="$RADARR_URL"
API_KEY="$API_KEY"
NEW_ROOT_FOLDER="$NEW_ROOT_FOLDER"
EOF
chmod 600 "$CONFIG_FILE" # Secure the config file
echo ""
echo "β
Configuration saved to $CONFIG_FILE"
echo ""
}
# Load or create configuration
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
else
setup_config
fi
echo "π‘ Fetching movie list from Radarr..."
# Fetch all movies from Radarr
ALL_MOVIES=$(curl -s -X GET "$RADARR_URL/api/v3/movie" \
-H "X-Api-Key: $API_KEY")
if [ -z "$ALL_MOVIES" ] || [ "$ALL_MOVIES" == "[]" ]; then
echo "Error: Failed to fetch movies or no movies found in Radarr"
exit 1
fi
# Filter movies not already in target root folder and create list
# Note: sizeOnDisk is in bytes, convert to GB with 2 decimal places
format_title() {
local title="$1" max_chars=65
local char_len=${#title}
# Use bash parameter expansion for char count (works for ASCII)
# For UTF-8, wc -m is more reliable
char_len=$(printf "%s" "$title" | wc -m | tr -d ' ')
if [ "$char_len" -gt "$max_chars" ]; then
title=$(printf "%s" "$title" | cut -c1-62)"..."
char_len=$max_chars
fi
local byte_len=${#title}
local width=$((max_chars + byte_len - char_len))
printf "%-*s" "$width" "$title"
}
# Extract movie data once (cache it)
MOVIE_DATA_RAW=$(echo "$ALL_MOVIES" | jq -r --arg target "$NEW_ROOT_FOLDER" \
'.[] | select(.path | startswith($target) | not) |
[.id, "\(.title) (\(.year // "Unknown"))", (((.sizeOnDisk // 0) / 1073741824 * 100 | floor) / 100), (.ratings.imdb.value // .ratings.tmdb.value // 0), .year] |
@tsv')
if [ -z "$MOVIE_DATA_RAW" ]; then
echo "All movies are already in the target root folder: $NEW_ROOT_FOLDER"
echo "No movies to process."
exit 0
fi
# Function to format movie list with current sort (uses cached data)
format_movie_list() {
local sort_mode="$1"
local sort_cmd
case "$sort_mode" in
title) sort_cmd="sort -t$'\t' -k2,2" ;;
size) sort_cmd="sort -t$'\t' -k3,3rn" ;;
rating) sort_cmd="sort -t$'\t' -k4,4rn" ;;
year) sort_cmd="sort -t$'\t' -k5,5rn" ;;
*) sort_cmd="sort -t$'\t' -k2,2" ;;
esac
echo "$MOVIE_DATA_RAW" | eval "$sort_cmd" | \
while IFS=$'\t' read -r id title size rating year; do
# Truncate title to 52 chars (leaving room for "...") if too long
char_len=$(printf "%s" "$title" | wc -m | tr -d ' ')
if [ "$char_len" -gt 55 ]; then
title=$(printf "%s" "$title" | cut -c1-52)"..."
fi
# Fix UTF-8 alignment: calculate byte length vs char length difference
byte_len=$(printf "%s" "$title" | wc -c | tr -d ' ')
char_len=$(printf "%s" "$title" | wc -m | tr -d ' ')
extra=$((byte_len - char_len))
width=$((55 + extra))
printf "%s:::%-*s %8.2f GB %4.1f\n" "$id" "$width" "$title" "$size" "$rating"
done
}
# Default sort mode
SORT_MODE="title"
# Main selection loop (allows re-sorting)
while true; do
MOVIE_LIST=$(format_movie_list "$SORT_MODE")
if [ -z "$MOVIE_LIST" ]; then
echo "Error formatting movie list"
exit 1
fi
MOVIE_COUNT=$(echo "$MOVIE_LIST" | wc -l | tr -d ' ')
# Column header for display
COLUMN_HEADER=$(printf "%-55s %11s %s" "TITLE" "SIZE" "RATING")
# Use fzf for interactive selection with reconfigure option
SELECTED=$(echo "$MOVIE_LIST" | fzf \
--multi \
--delimiter=':::' \
--with-nth=2 \
--height=80% \
--border \
--header="$COLUMN_HEADER
Target: $NEW_ROOT_FOLDER | $MOVIE_COUNT movies | Sort: $SORT_MODE
Tab=select | Ctrl-S=sort | Ctrl-R=reconfigure" \
--header-first \
--prompt="π¬ " \
--pointer="βΆ" \
--bind="ctrl-r:become(echo __RECONFIGURE__)" \
--bind="ctrl-s:become(echo __SORT__)")
# Check if user wants to reconfigure
if [ "$SELECTED" = "__RECONFIGURE__" ]; then
setup_config
source "$CONFIG_FILE"
exec "$0" # Restart the script
fi
# Check if user wants to change sort
if [ "$SELECTED" = "__SORT__" ]; then
SORT_MODE=$(printf "title\nsize\nrating\nyear" | fzf \
--height=30% \
--border \
--header="Select sort order:" \
--prompt="π ")
[ -z "$SORT_MODE" ] && SORT_MODE="title"
continue
fi
# Exit loop if user made a selection or cancelled
break
done
# Check if user cancelled selection
if [ -z "$SELECTED" ]; then
echo "Operation cancelled"
exit 0
fi
# Count selected movies
SELECTED_COUNT=$(echo "$SELECTED" | wc -l | tr -d ' ')
echo ""
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββ"
echo "Selected $SELECTED_COUNT movie(s)"
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββ"
echo ""
# Confirm action
read -p "β οΈ This will delete movie files and move to '$NEW_ROOT_FOLDER'. Continue? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Operation cancelled"
exit 0
fi
echo ""
# Process each selected movie
CURRENT=0
echo "$SELECTED" | while IFS= read -r line; do
CURRENT=$((CURRENT + 1))
# Extract movie ID and title from selection
MOVIE_ID=$(echo "$line" | cut -d':' -f1)
MOVIE_TITLE=$(echo "$line" | cut -d':' -f4-)
echo ""
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββ"
echo "Processing [$CURRENT/$SELECTED_COUNT]: $MOVIE_TITLE (ID: $MOVIE_ID)"
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββ"
# Step 1: Get movie details
echo "π₯ Fetching movie details..."
MOVIE_DATA=$(curl -s -X GET "$RADARR_URL/api/v3/movie/$MOVIE_ID" \
-H "X-Api-Key: $API_KEY")
if [ -z "$MOVIE_DATA" ]; then
echo "β Error: Failed to fetch movie details"
continue
fi
echo "β Movie details retrieved"
# Step 2: Get and delete all movie files
echo "ποΈ Fetching movie files..."
MOVIE_FILES=$(curl -s -X GET "$RADARR_URL/api/v3/moviefile?movieId=$MOVIE_ID" \
-H "X-Api-Key: $API_KEY")
FILE_IDS=$(echo "$MOVIE_FILES" | jq -r '.[].id')
if [ -z "$FILE_IDS" ]; then
echo "β οΈ No movie files found to delete"
else
FILE_COUNT=$(echo "$FILE_IDS" | wc -l | xargs)
echo "ποΈ Deleting $FILE_COUNT movie file(s)..."
for FILE_ID in $FILE_IDS; do
curl -s -X DELETE "$RADARR_URL/api/v3/moviefile/$FILE_ID" \
-H "X-Api-Key: $API_KEY" > /dev/null
echo " β Deleted file ID: $FILE_ID"
done
echo "β All movie files deleted"
fi
# Step 3: Update movie with new root folder
echo "π Updating root folder to: $NEW_ROOT_FOLDER"
# Get movie title and year for the new path in one jq call
read -r MOVIE_TITLE_FOR_PATH MOVIE_YEAR <<< $(echo "$MOVIE_DATA" | jq -r '[.title, .year] | @tsv')
NEW_PATH="$NEW_ROOT_FOLDER/$MOVIE_TITLE_FOR_PATH ($MOVIE_YEAR)"
echo " New path: $NEW_PATH"
# Parse and update movie data with new root folder and path
UPDATED_MOVIE=$(echo "$MOVIE_DATA" | jq --arg path "$NEW_PATH" --arg root "$NEW_ROOT_FOLDER" '.path = $path | .rootFolderPath = $root')
UPDATE_RESPONSE=$(curl -s -X PUT "$RADARR_URL/api/v3/movie/$MOVIE_ID" \
-H "X-Api-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d "$UPDATED_MOVIE")
if [ $? -eq 0 ]; then
echo "β Root folder updated"
else
echo "β Error: Failed to update root folder"
continue
fi
# Step 4: Trigger movie search
echo "π Starting movie search..."
SEARCH_RESPONSE=$(curl -s -X POST "$RADARR_URL/api/v3/command" \
-H "X-Api-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d "{\"name\":\"MoviesSearch\",\"movieIds\":[$MOVIE_ID]}")
echo "β Search initiated"
echo "β
Done processing '$MOVIE_TITLE'"
done
echo ""
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββ"
echo "β
All movies processed successfully!"
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββ"