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()