main
  1"""
  2Update artist metadata profile settings in Lidarr.
  3
  4This script allows you to change the metadata profile for artists in Lidarr.
  5You can update all artists or filter by name pattern.
  6"""
  7
  8from typing import Any, Dict, List
  9
 10from lib import (
 11    ArrClient,
 12    CommandContext,
 13    get_confirmation_decision,
 14    print_item_list,
 15    print_section_header,
 16)
 17
 18
 19def get_artists(client: ArrClient) -> List[Dict[str, Any]]:
 20    """Get all artists from Lidarr."""
 21    return client.get("/api/v1/artist")
 22
 23
 24def get_metadata_profiles(client: ArrClient) -> List[Dict[str, Any]]:
 25    """Get all metadata profiles from Lidarr."""
 26    return client.get("/api/v1/metadataprofile")
 27
 28
 29def filter_artists(
 30    artists: List[Dict[str, Any]], pattern: str = None
 31) -> List[Dict[str, Any]]:
 32    """
 33    Filter artists by name pattern.
 34
 35    Args:
 36        artists: List of artist objects
 37        pattern: Optional substring to match in artist names (case-insensitive)
 38
 39    Returns:
 40        Filtered list of artists
 41    """
 42    if not pattern:
 43        return artists
 44
 45    pattern_lower = pattern.lower()
 46    return [
 47        artist
 48        for artist in artists
 49        if pattern_lower in artist.get("artistName", "").lower()
 50    ]
 51
 52
 53def update_artist_metadata_profile(
 54    client: ArrClient, artist: Dict[str, Any], metadata_profile_id: int
 55) -> bool:
 56    """
 57    Update an artist's metadata profile.
 58
 59    Args:
 60        client: Lidarr API client
 61        artist: Artist object to update
 62        metadata_profile_id: Metadata profile ID to set
 63
 64    Returns:
 65        True if successful, False otherwise
 66    """
 67    # Update the artist object with new metadata profile
 68    artist["metadataProfileId"] = metadata_profile_id
 69
 70    # Send PUT request to update the artist
 71    result = client.put("/api/v1/artist", artist)
 72
 73    return bool(result and result.get("id"))
 74
 75
 76def run(
 77    lidarr_url: str,
 78    lidarr_api_key: str,
 79    metadata_profile_name: str,
 80    artist_pattern: str,
 81    dry_run: bool,
 82    no_confirm: bool,
 83):
 84    """Execute the lidarr update-metadata-profile command."""
 85    # Create client and context
 86    lidarr = ArrClient(lidarr_url, lidarr_api_key)
 87    ctx = CommandContext(dry_run, no_confirm)
 88
 89    # Fetch metadata profiles
 90    print_section_header("FETCHING METADATA PROFILES")
 91    print("Retrieving metadata profiles...")
 92    metadata_profiles = get_metadata_profiles(lidarr)
 93
 94    if not metadata_profiles:
 95        print("\nNo metadata profiles found in Lidarr!")
 96        return
 97
 98    print(f"Found {len(metadata_profiles)} metadata profile(s):\n")
 99    for profile in metadata_profiles:
100        print(f"  - {profile.get('name')} (ID: {profile.get('id')})")
101
102    # Find the target metadata profile
103    target_profile = None
104    if metadata_profile_name:
105        # Search by name (case-insensitive)
106        for profile in metadata_profiles:
107            profile_name = profile.get("name", "").lower()
108            if profile_name == metadata_profile_name.lower():
109                target_profile = profile
110                break
111
112        if not target_profile:
113            print(
114                f"\nError: Metadata profile '{metadata_profile_name}' "
115                "not found!"
116            )
117            print("\nAvailable profiles:")
118            for profile in metadata_profiles:
119                print(f"  - {profile.get('name')}")
120            return
121    else:
122        # Use the first profile as default
123        target_profile = metadata_profiles[0]
124        print(
125            f"\nNo profile specified, using default: "
126            f"{target_profile.get('name')}"
127        )
128
129    metadata_profile_id = target_profile.get("id")
130    metadata_profile_name = target_profile.get("name")
131
132    print(
133        f"\nTarget metadata profile: {metadata_profile_name} "
134        f"(ID: {metadata_profile_id})"
135    )
136
137    # Fetch all artists
138    print_section_header("FETCHING ARTISTS FROM LIDARR")
139    print("Retrieving artists...")
140    all_artists = get_artists(lidarr)
141
142    if not all_artists:
143        print("\nNo artists found in Lidarr!")
144        return
145
146    print(f"Found {len(all_artists)} total artists in Lidarr")
147
148    # Filter artists if pattern provided
149    if artist_pattern:
150        print(f"\nFiltering artists by pattern: '{artist_pattern}'")
151        filtered_artists = filter_artists(all_artists, artist_pattern)
152        print(f"Matched {len(filtered_artists)} artists")
153    else:
154        filtered_artists = all_artists
155        print("\nNo filter specified - will update ALL artists")
156
157    if not filtered_artists:
158        print("\nNo artists match the specified pattern!")
159        return
160
161    # Display artists to be updated
162    print_section_header("ARTISTS TO UPDATE")
163    artist_names = [
164        artist.get("artistName", "Unknown") for artist in filtered_artists
165    ]
166    prefix_msg = (
167        f"Artists that will be set to metadata profile "
168        f"'{metadata_profile_name}'"
169    )
170    print_item_list(artist_names, prefix_msg, max_display=20)
171
172    # Confirm operation
173    confirm_msg = (
174        f"\nUpdate metadata profile for {len(filtered_artists)} "
175        f"artist(s) to '{metadata_profile_name}'?"
176    )
177    if not get_confirmation_decision(ctx, confirm_msg):
178        if not ctx.dry_run:
179            print("Operation cancelled")
180        return
181
182    # Update artists
183    print_section_header("UPDATING ARTISTS")
184
185    updated_count = 0
186    failed_count = 0
187
188    for idx, artist in enumerate(filtered_artists, 1):
189        artist_name = artist.get("artistName", "Unknown")
190        current_profile_id = artist.get("metadataProfileId", "Unknown")
191        print(
192            f"[{idx}/{len(filtered_artists)}] {artist_name} "
193            f"(current profile ID: {current_profile_id})"
194        )
195
196        if not ctx.dry_run:
197            success = update_artist_metadata_profile(
198                lidarr, artist, metadata_profile_id
199            )
200            if success:
201                print(
202                    f"  ✓ Updated to metadata profile "
203                    f"'{metadata_profile_name}'"
204                )
205                updated_count += 1
206            else:
207                print("  ✗ Failed to update")
208                failed_count += 1
209        else:
210            print(
211                f"  [DRY RUN] Would update to metadata profile "
212                f"'{metadata_profile_name}'"
213            )
214            updated_count += 1
215
216    # Final summary
217    print_section_header("FINAL SUMMARY")
218    print(f"\nTotal artists: {len(all_artists)}")
219    print(f"Artists selected: {len(filtered_artists)}")
220    print(f"  - Successfully updated: {updated_count}")
221    if failed_count > 0:
222        print(f"  - Failed: {failed_count}")
223
224    if ctx.dry_run:
225        print(
226            "\n[DRY RUN] No changes were made. "
227            "Remove --dry-run to update artists."
228        )
229    elif updated_count > 0:
230        print(
231            f"\nArtists are now set to metadata profile: "
232            f"{metadata_profile_name}\n"
233            "This will affect which album types and releases are "
234            "monitored and downloaded."
235        )