system-manager-wakasu
1#!/usr/bin/env -S uv run --quiet --script
2# /// script
3# dependencies = [
4# "requests",
5# ]
6# ///
7"""
8Rename movies in Radarr with interactive confirmation.
9
10This script:
111. Fetches all movies from Radarr API
122. Checks which movies have files that need renaming
133. Previews the rename changes for each movie
144. Asks for confirmation before applying renames
15
16Usage:
17 ./radarr-rename-movies.py <radarr_url> <api_key>
18
19Example:
20 ./radarr-rename-movies.py http://localhost:7878 your-api-key
21"""
22
23from typing import Any, Dict, List
24
25from arrlib import (
26 ArrClient,
27 create_arr_parser,
28 get_confirmation_decision,
29 print_final_summary,
30 print_item_list,
31 print_section_header,
32)
33
34
35def get_rename_preview(
36 client: ArrClient, movie_id: int
37) -> List[Dict[str, Any]]:
38 """Get preview of files that will be renamed for a movie."""
39 return client.get("/api/v3/rename", params={"movieId": movie_id})
40
41
42def execute_rename(client: ArrClient, movie_id: int) -> Dict[str, Any]:
43 """Execute rename operation for a movie."""
44 payload = {"name": "RenameMovie", "movieIds": [movie_id]}
45 return client.post("/api/v3/command", payload)
46
47
48def main():
49 parser = create_arr_parser(
50 "Radarr", "Rename Radarr movies with confirmation", 7878
51 )
52 args = parser.parse_args()
53
54 # Create client
55 client = ArrClient(args.radarr_url, args.api_key)
56
57 print(f"Fetching movies from {client.base_url}...")
58 all_movies = client.get("/api/v3/movie")
59 print(f"Found {len(all_movies)} movies\n")
60
61 movies_with_renames = []
62 movies_without_renames = []
63
64 # Check each movie for rename candidates
65 print("Checking which movies need renaming...")
66 for movie in all_movies:
67 movie_id = movie.get("id")
68 movie_title = movie.get("title", "Unknown")
69 year = movie.get("year", "")
70 display_title = (
71 f"{movie_title} ({year})" if year else movie_title
72 )
73
74 rename_preview = get_rename_preview(client, movie_id)
75
76 if rename_preview:
77 movies_with_renames.append(
78 (movie_id, display_title, rename_preview)
79 )
80 else:
81 movies_without_renames.append(display_title)
82
83 # Print summary
84 print_section_header("SUMMARY")
85 print_item_list(movies_without_renames, "✓ No renames needed")
86
87 if movies_with_renames:
88 count = len(movies_with_renames)
89 print(f"\n→ Movies with renames needed: {count}")
90
91 if not movies_with_renames:
92 print("\nNo movies need renaming!")
93 return
94
95 # Process each movie that needs renaming
96 print_section_header("RENAME PREVIEW")
97
98 renamed_count = 0
99 skipped_count = 0
100
101 for movie_id, movie_title, rename_preview in movies_with_renames:
102 print(f"\n{'=' * 80}")
103 print(f"Movie: {movie_title}")
104 print(f"Files to rename: {len(rename_preview)}")
105 print("=" * 80)
106
107 # Show preview of renames
108 for i, item in enumerate(rename_preview):
109 existing_path = item.get("existingPath", "Unknown")
110 new_path = item.get("newPath", "Unknown")
111 print(f"\n File {i + 1}:")
112 print(f" FROM: {existing_path}")
113 print(f" TO: {new_path}")
114
115 # Ask for confirmation
116 file_word = "file" if len(rename_preview) == 1 else "files"
117 prompt = (
118 f"\nRename {len(rename_preview)} {file_word} "
119 f"for '{movie_title}'?"
120 )
121 should_rename = get_confirmation_decision(args, prompt)
122
123 if should_rename:
124 print("Executing rename...")
125 result = execute_rename(client, movie_id)
126 if result:
127 print("✓ Rename command queued successfully")
128 renamed_count += 1
129 else:
130 print("✗ Failed to queue rename command")
131 skipped_count += 1
132 else:
133 if not args.dry_run:
134 print("Skipped")
135 skipped_count += 1
136
137 # Final summary
138 if args.dry_run:
139 print_section_header("FINAL SUMMARY")
140 print(
141 f"\n[DRY RUN] Found {len(movies_with_renames)} movies "
142 "that need renaming"
143 )
144 print("No changes were made. Remove --dry-run to apply renames.")
145 else:
146 print_final_summary(
147 len(movies_with_renames),
148 renamed_count,
149 skipped_count,
150 "Renamed",
151 )
152
153
154if __name__ == "__main__":
155 main()