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 )