nftable-migration
1#!/usr/bin/env python3
2"""
3arr - Unified CLI for managing *arr services (Sonarr, Radarr, Lidarr).
4
5This tool provides a consistent interface for common operations across
6the *arr media management stack.
7"""
8
9import sys
10from pathlib import Path
11
12import click
13
14# Add the arr package directory to Python path
15ARR_DIR = Path(__file__).parent.resolve()
16sys.path.insert(0, str(ARR_DIR))
17
18
19@click.group()
20def cli():
21 """Unified CLI for managing *arr services (Sonarr, Radarr, Lidarr).
22
23 This tool provides a consistent interface for common operations across
24 the *arr media management stack.
25 """
26 pass
27
28
29@cli.group()
30def sonarr():
31 """Manage Sonarr TV series."""
32 pass
33
34
35@cli.group()
36def radarr():
37 """Manage Radarr movies."""
38 pass
39
40
41@cli.group()
42def lidarr():
43 """Manage Lidarr music."""
44 pass
45
46
47@sonarr.command()
48@click.argument("url")
49@click.argument("api_key")
50@click.option("--dry-run", is_flag=True, help="Show what would be changed without making changes")
51@click.option("--no-confirm", "--yolo", is_flag=True, help="Skip interactive confirmation (use with caution)")
52def rename(url, api_key, dry_run, no_confirm):
53 """Rename series episodes.
54
55 Examples:
56 arr sonarr rename http://localhost:8989 your-api-key
57 arr sonarr rename http://localhost:8989 your-api-key --dry-run
58 """
59 from commands import sonarr_rename
60 sonarr_rename.run(url, api_key, dry_run, no_confirm)
61
62
63@radarr.command()
64@click.argument("url")
65@click.argument("api_key")
66@click.option("--dry-run", is_flag=True, help="Show what would be changed without making changes")
67@click.option("--no-confirm", "--yolo", is_flag=True, help="Skip interactive confirmation (use with caution)")
68def rename(url, api_key, dry_run, no_confirm):
69 """Rename movies.
70
71 Examples:
72 arr radarr rename http://localhost:7878 your-api-key
73 arr radarr rename http://localhost:7878 your-api-key --dry-run
74 """
75 from commands import radarr_rename
76 radarr_rename.run(url, api_key, dry_run, no_confirm)
77
78
79@lidarr.command("rename-albums")
80@click.argument("url")
81@click.argument("api_key")
82@click.option("--dry-run", is_flag=True, help="Show what would be changed without making changes")
83@click.option("--no-confirm", "--yolo", is_flag=True, help="Skip interactive confirmation (use with caution)")
84def lidarr_rename_albums(url, api_key, dry_run, no_confirm):
85 """Rename albums.
86
87 Examples:
88 arr lidarr rename-albums http://localhost:8686 your-api-key
89 arr lidarr rename-albums http://localhost:8686 your-api-key --dry-run
90 """
91 from commands import lidarr_rename_albums
92 lidarr_rename_albums.run(url, api_key, dry_run, no_confirm)
93
94
95@lidarr.command("retag-albums")
96@click.argument("url")
97@click.argument("api_key")
98@click.option("--dry-run", is_flag=True, help="Show what would be changed without making changes")
99@click.option("--no-confirm", "--yolo", is_flag=True, help="Skip interactive confirmation (use with caution)")
100def lidarr_retag_albums(url, api_key, dry_run, no_confirm):
101 """Retag albums metadata.
102
103 Examples:
104 arr lidarr retag-albums http://localhost:8686 your-api-key
105 arr lidarr retag-albums http://localhost:8686 your-api-key --dry-run
106 """
107 from commands import lidarr_retag_albums
108 lidarr_retag_albums.run(url, api_key, dry_run, no_confirm)
109
110
111@lidarr.command("update-paths")
112@click.argument("url")
113@click.argument("api_key")
114@click.argument("music_folder")
115@click.option("--dry-run", is_flag=True, help="Show what would be updated without making changes")
116def lidarr_update_paths(url, api_key, music_folder, dry_run):
117 """Update library paths.
118
119 Examples:
120 arr lidarr update-paths http://localhost:8686 your-api-key /data/music
121 arr lidarr update-paths http://localhost:8686 your-api-key /data/music --dry-run
122 """
123 from commands import lidarr_update_paths
124 lidarr_update_paths.run(url, api_key, music_folder, dry_run)
125
126
127@lidarr.command("sync-spotify")
128@click.argument("url")
129@click.option("--api-key", "-k", envvar="LIDARR_API_KEY", required=True, help="Lidarr API key")
130@click.option("--spotify-client-id", envvar="SPOTIFY_CLIENT_ID", required=True, help="Spotify application client ID")
131@click.option("--spotify-client-secret", envvar="SPOTIFY_CLIENT_SECRET", required=True, help="Spotify application client secret")
132@click.option("--spotify-username", "-u", envvar="SPOTIFY_USERNAME", help="Spotify username for interactive mode (to list your playlists)")
133@click.argument("playlist_ids", nargs=-1, required=False)
134@click.option("--root-folder", default="/music", help="Root folder for music in Lidarr")
135@click.option("--monitor", default="all", type=click.Choice(["all", "future", "missing", "existing", "none"]), help="Album monitoring mode")
136@click.option("--request-delay", default=1.5, type=float, help="Delay between Lidarr API requests (seconds)")
137@click.option("--dry-run", is_flag=True, help="Show what would be added without making changes")
138@click.option("--no-confirm", "--yolo", is_flag=True, help="Skip interactive confirmation (use with caution)")
139def lidarr_sync_spotify(url, api_key, spotify_client_id, spotify_client_secret, spotify_username, playlist_ids, root_folder, monitor, request_delay, dry_run, no_confirm):
140 """Sync Spotify playlists to Lidarr.
141
142 Fetches tracks from Spotify playlists and adds missing artists to Lidarr.
143
144 URL: Lidarr server URL (e.g., http://localhost:8686)
145
146 Credentials can be provided via flags or environment variables:
147 - LIDARR_API_KEY
148 - SPOTIFY_CLIENT_ID (get from https://developer.spotify.com/dashboard)
149 - SPOTIFY_CLIENT_SECRET
150 - SPOTIFY_USERNAME (optional, for interactive mode)
151
152 Interactive mode: If no PLAYLIST_IDS are provided and --spotify-username
153 is set, you'll be prompted to select from your public playlists using fzf.
154
155 Examples:
156 # Interactive mode - select from your playlists
157 export SPOTIFY_USERNAME=your-spotify-username
158 arr lidarr sync-spotify http://localhost:8686 -u your-username
159
160 # Or with environment variables
161 export LIDARR_API_KEY=your-lidarr-key
162 export SPOTIFY_CLIENT_ID=your-client-id
163 export SPOTIFY_CLIENT_SECRET=your-client-secret
164 export SPOTIFY_USERNAME=your-username
165 arr lidarr sync-spotify http://localhost:8686
166
167 # Sync specific playlists (no username needed)
168 arr lidarr sync-spotify http://localhost:8686 \\
169 37i9dQZF1DXcBWIGoYBM5M 37i9dQZF1DX0XUsuxWHRQd
170
171 # Dry run
172 arr lidarr sync-spotify http://localhost:8686 -u username --dry-run
173
174 # Monitor only future albums
175 arr lidarr sync-spotify http://localhost:8686 \\
176 -u username --monitor future
177 """
178 from commands import lidarr_sync_spotify
179 lidarr_sync_spotify.run(
180 url,
181 api_key,
182 spotify_client_id,
183 spotify_client_secret,
184 spotify_username,
185 list(playlist_ids),
186 root_folder,
187 monitor,
188 request_delay,
189 dry_run,
190 no_confirm
191 )
192
193
194if __name__ == "__main__":
195 cli()