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