main
1"""
2Update artist monitoring settings in Lidarr.
3
4This script allows you to change the monitoring mode 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 filter_artists(
25 artists: List[Dict[str, Any]], pattern: str = None
26) -> List[Dict[str, Any]]:
27 """
28 Filter artists by name pattern.
29
30 Args:
31 artists: List of artist objects
32 pattern: Optional substring to match in artist names (case-insensitive)
33
34 Returns:
35 Filtered list of artists
36 """
37 if not pattern:
38 return artists
39
40 pattern_lower = pattern.lower()
41 return [
42 artist
43 for artist in artists
44 if pattern_lower in artist.get("artistName", "").lower()
45 ]
46
47
48def update_artist_monitoring(
49 client: ArrClient,
50 artist: Dict[str, Any],
51 monitor_mode: str,
52 monitor_new_items: str = None,
53) -> bool:
54 """
55 Update an artist's monitoring settings.
56
57 Args:
58 client: Lidarr API client
59 artist: Artist object to update
60 monitor_mode: Monitoring mode (all, future, missing, existing, none)
61 monitor_new_items: Monitor new items mode (all, new, none) - optional
62
63 Returns:
64 True if successful, False otherwise
65 """
66 # Update the artist object with new monitoring settings
67 artist["addOptions"] = {"monitor": monitor_mode}
68
69 # Update monitorNewItems if specified
70 if monitor_new_items is not None:
71 artist["monitorNewItems"] = monitor_new_items
72
73 # Send PUT request to update the artist
74 result = client.put("/api/v1/artist", artist)
75
76 return bool(result and result.get("id"))
77
78
79def run(
80 lidarr_url: str,
81 lidarr_api_key: str,
82 monitor_mode: str,
83 monitor_new_items: str,
84 artist_pattern: str,
85 dry_run: bool,
86 no_confirm: bool,
87):
88 """Execute the lidarr update-monitoring command."""
89 # Create client and context
90 lidarr = ArrClient(lidarr_url, lidarr_api_key)
91 ctx = CommandContext(dry_run, no_confirm)
92
93 # Fetch all artists
94 print_section_header("FETCHING ARTISTS FROM LIDARR")
95 print("Retrieving artists...")
96 all_artists = get_artists(lidarr)
97
98 if not all_artists:
99 print("\nNo artists found in Lidarr!")
100 return
101
102 print(f"Found {len(all_artists)} total artists in Lidarr")
103
104 # Filter artists if pattern provided
105 if artist_pattern:
106 print(f"\nFiltering artists by pattern: '{artist_pattern}'")
107 filtered_artists = filter_artists(all_artists, artist_pattern)
108 print(f"Matched {len(filtered_artists)} artists")
109 else:
110 filtered_artists = all_artists
111 print("\nNo filter specified - will update ALL artists")
112
113 if not filtered_artists:
114 print("\nNo artists match the specified pattern!")
115 return
116
117 # Display artists to be updated
118 print_section_header("ARTISTS TO UPDATE")
119 artist_names = [
120 artist.get("artistName", "Unknown") for artist in filtered_artists
121 ]
122
123 # Build description of what will be updated
124 update_desc = f"monitor mode '{monitor_mode}'"
125 if monitor_new_items:
126 update_desc += f" and monitor new items '{monitor_new_items}'"
127
128 print_item_list(
129 artist_names,
130 f"Artists that will be set to {update_desc}",
131 max_display=20,
132 )
133
134 # Confirm operation
135 confirm_msg = (
136 f"\nUpdate monitoring for {len(filtered_artists)} artist(s) "
137 f"to '{monitor_mode}'"
138 )
139 if monitor_new_items:
140 confirm_msg += f" (monitor new items: '{monitor_new_items}')"
141 confirm_msg += "?"
142
143 if not get_confirmation_decision(ctx, confirm_msg):
144 if not ctx.dry_run:
145 print("Operation cancelled")
146 return
147
148 # Update artists
149 print_section_header("UPDATING ARTISTS")
150
151 updated_count = 0
152 failed_count = 0
153
154 for idx, artist in enumerate(filtered_artists, 1):
155 artist_name = artist.get("artistName", "Unknown")
156 current_monitor_new = artist.get("monitorNewItems", "Unknown")
157 print(
158 f"[{idx}/{len(filtered_artists)}] {artist_name} "
159 f"(current monitorNewItems: {current_monitor_new})"
160 )
161
162 if not ctx.dry_run:
163 success = update_artist_monitoring(
164 lidarr, artist, monitor_mode, monitor_new_items
165 )
166 if success:
167 msg = f" ✓ Updated to monitor '{monitor_mode}'"
168 if monitor_new_items:
169 msg += f" (monitor new items: '{monitor_new_items}')"
170 print(msg)
171 updated_count += 1
172 else:
173 print(" ✗ Failed to update")
174 failed_count += 1
175 else:
176 msg = f" [DRY RUN] Would update to monitor '{monitor_mode}'"
177 if monitor_new_items:
178 msg += f" (monitor new items: '{monitor_new_items}')"
179 print(msg)
180 updated_count += 1
181
182 # Final summary
183 print_section_header("FINAL SUMMARY")
184 print(f"\nTotal artists: {len(all_artists)}")
185 print(f"Artists selected: {len(filtered_artists)}")
186 print(f" - Successfully updated: {updated_count}")
187 if failed_count > 0:
188 print(f" - Failed: {failed_count}")
189
190 if ctx.dry_run:
191 print(
192 "\n[DRY RUN] No changes were made. "
193 "Remove --dry-run to update artists."
194 )
195 elif updated_count > 0:
196 summary_msg = f"\nArtists are now set to monitor: {monitor_mode}"
197 if monitor_new_items:
198 summary_msg += f"\nMonitor new items: {monitor_new_items}"
199 summary_msg += (
200 "\nLidarr will automatically search for albums based on "
201 "these settings."
202 )
203 print(summary_msg)