222 lines
5.7 KiB
Python
Executable File
222 lines
5.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
from requests.adapters import HTTPAdapter
|
|
from urllib3.util.retry import Retry
|
|
import requests
|
|
import re
|
|
import time
|
|
|
|
# ============================================================================
|
|
# CONFIG
|
|
# ============================================================================
|
|
ip_address = "192.168.0.4"
|
|
INSTANCES = [
|
|
{
|
|
"name": "radarr-de",
|
|
"url": f"http://{ip_address}:7879",
|
|
"api_key": "29f67f0ac8894357b22ea3f7fc1abfdf",
|
|
},
|
|
{
|
|
"name": "radarr-en",
|
|
"url": f"http://{ip_address}:7878",
|
|
"api_key": "4332aee075c748f2acee41aa26f06007",
|
|
},
|
|
{
|
|
"name": "sonarr",
|
|
"url": f"http://{ip_address}:8990",
|
|
"api_key": "707d4fcbb3d74822ba990c6a8b7278e9",
|
|
},
|
|
{
|
|
"name": "lidarr",
|
|
"url": f"http://{ip_address}:8686",
|
|
"api_key": "bb41cbf2b17e439290f90ecfaeb24dff",
|
|
},
|
|
# {
|
|
# "name": "listenarr",
|
|
# "url": f"http://{ip_address}:7878",
|
|
# "api_key": "READARR_API_KEY",
|
|
# },
|
|
]
|
|
|
|
PRIVATE_CLIENT = "qbittorrent-private"
|
|
PUBLIC_CLIENT = "qbittorrent-public"
|
|
|
|
PRIVATE_INDEXER_REGEX = re.compile(
|
|
r"(Bittorrentfiles|NordicQuality|funzone|SeedCore|The Brothers)",
|
|
re.IGNORECASE
|
|
)
|
|
|
|
DRY_RUN = False
|
|
|
|
# ============================================================================
|
|
|
|
session = requests.Session()
|
|
|
|
retry_strategy = Retry(
|
|
total=3,
|
|
connect=3,
|
|
read=3,
|
|
backoff_factor=2,
|
|
status_forcelist=[429, 500, 502, 503, 504],
|
|
allowed_methods=["GET", "PUT", "POST"],
|
|
)
|
|
|
|
adapter = HTTPAdapter(max_retries=retry_strategy)
|
|
|
|
session.mount("http://", adapter)
|
|
session.mount("https://", adapter)
|
|
|
|
|
|
def get_api_version(base_url, api_key):
|
|
r = requests.get(
|
|
f"{base_url}/api",
|
|
headers={"X-Api-Key": api_key},
|
|
timeout=30,
|
|
)
|
|
r.raise_for_status()
|
|
return r.json()["current"]
|
|
|
|
def api_get(base_url, api_key, endpoint, retries=3):
|
|
last_error = None
|
|
|
|
for attempt in range(1, retries + 1):
|
|
try:
|
|
r = requests.get(
|
|
f"{base_url}{endpoint}",
|
|
headers={"X-Api-Key": api_key},
|
|
timeout=30,
|
|
)
|
|
r.raise_for_status()
|
|
return r.json()
|
|
|
|
except requests.HTTPError as e:
|
|
print("Status:", e.response.status_code)
|
|
print("Response:", e.response.text)
|
|
raise
|
|
except Exception as e:
|
|
last_error = e
|
|
print(f"GET failed ({attempt}/{retries}): {e}")
|
|
|
|
if attempt < retries:
|
|
time.sleep(2 ** attempt)
|
|
|
|
raise last_error
|
|
|
|
|
|
def api_put(base_url, api_key, endpoint, payload, retries=3):
|
|
last_error = None
|
|
|
|
for attempt in range(1, retries + 1):
|
|
try:
|
|
r = requests.put(
|
|
f"{base_url}{endpoint}",
|
|
headers={
|
|
"X-Api-Key": api_key,
|
|
"Content-Type": "application/json",
|
|
},
|
|
json=payload,
|
|
timeout=30,
|
|
)
|
|
r.raise_for_status()
|
|
return r.json() if r.content else None
|
|
except requests.HTTPError as e:
|
|
print("Status:", e.response.status_code)
|
|
print("Response:", e.response.text)
|
|
raise
|
|
except Exception as e:
|
|
last_error = e
|
|
print(f"PUT failed ({attempt}/{retries}): {e}")
|
|
|
|
if attempt < retries:
|
|
time.sleep(2 ** attempt)
|
|
|
|
raise last_error
|
|
|
|
def get_download_client_ids(base_url, endpoint,api_key):
|
|
clients = api_get(base_url, api_key, endpoint)
|
|
|
|
private_id = None
|
|
public_id = None
|
|
|
|
for client in clients:
|
|
if client["name"] == PRIVATE_CLIENT:
|
|
private_id = client["id"]
|
|
|
|
if client["name"] == PUBLIC_CLIENT:
|
|
public_id = client["id"]
|
|
|
|
return private_id, public_id
|
|
|
|
|
|
def process_instance(instance):
|
|
name = instance["name"]
|
|
url = instance["url"]
|
|
api_key = instance["api_key"]
|
|
|
|
print(f"\n{'=' * 80}")
|
|
print(f"Processing: {name}")
|
|
print(f"{'=' * 80}")
|
|
|
|
api_version = get_api_version(url, api_key)
|
|
endpoint_download = f"/api/{api_version}/downloadclient"
|
|
|
|
private_id, public_id = get_download_client_ids(url, endpoint_download, api_key)
|
|
|
|
if not private_id:
|
|
print(f"ERROR: '{PRIVATE_CLIENT}' not found")
|
|
return
|
|
|
|
if not public_id:
|
|
print(f"ERROR: '{PUBLIC_CLIENT}' not found")
|
|
return
|
|
|
|
print(f"{PRIVATE_CLIENT} id: {private_id}")
|
|
print(f"{PUBLIC_CLIENT} id: {public_id}")
|
|
|
|
indexers = api_get(url,api_key,f"/api/{api_version}/indexer")
|
|
for indexer in indexers:
|
|
idx_id = indexer["id"]
|
|
idx_name = indexer["name"]
|
|
print(idx_name)
|
|
|
|
if PRIVATE_INDEXER_REGEX.search(idx_name):
|
|
target_id = private_id
|
|
target_name = PRIVATE_CLIENT
|
|
else:
|
|
target_id = public_id
|
|
target_name = PUBLIC_CLIENT
|
|
|
|
current_id = indexer.get("downloadClientId")
|
|
|
|
if current_id == target_id:
|
|
print(f"SKIP {idx_name} -> {target_name}")
|
|
continue
|
|
|
|
indexer["downloadClientId"] = target_id
|
|
|
|
if DRY_RUN:
|
|
print(f"DRY {idx_name} -> {target_name}")
|
|
continue
|
|
endpoint_indexer = f"/api/{api_version}/indexer/{idx_id}?forceSave=true"
|
|
|
|
try:
|
|
api_put(
|
|
url,
|
|
api_key,
|
|
endpoint_indexer,
|
|
indexer,
|
|
)
|
|
|
|
print(f"SET {idx_name} -> {target_name}")
|
|
|
|
except Exception as e:
|
|
print(f"FAIL {idx_name}: {e}")
|
|
|
|
|
|
def main():
|
|
for instance in INSTANCES:
|
|
process_instance(instance)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |