Move embedded Python scripts out of Nix string interpolation into standalone files under scripts/. Each script reads its configuration from a JSON file passed as the first CLI argument. Shared utilities (API key reading, API polling, health check loop) are consolidated into common.py, eliminating three copies of read_api_key and wait_for_api. Implementation improvements included in the extraction: - Remove pyarr dependency; all HTTP calls use raw requests - Add update semantics: download clients and synced apps are now compared against desired state and updated on drift via PUT - Bazarr configure_provider compares API keys and updates stale ones - Narrow health_check_loop exception clause from bare Exception to (RequestException, ValueError, KeyError) - Fix double resp.json() call in resolve_profile_id (jellyseerr) - Replace os.system with subprocess.run for Jellyseerr restart - Handle Servarr fields with missing 'value' key - Skip masked fields (privacy=apiKey/password) in drift detection to prevent spurious updates every run
103 lines
2.9 KiB
Python
103 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Declarative API initialization for Bazarr provider connections.
|
|
|
|
Idempotently configures Sonarr and Radarr providers in Bazarr via its
|
|
settings API. Detects stale API keys and updates them.
|
|
"""
|
|
|
|
import os
|
|
|
|
import requests as http
|
|
|
|
from common import (
|
|
load_config,
|
|
read_api_key_xml,
|
|
read_api_key_yaml,
|
|
wait_for_api,
|
|
)
|
|
|
|
|
|
def configure_provider(base_url, api_key, provider_type, provider_config):
|
|
"""Idempotently configure a Sonarr/Radarr provider in Bazarr.
|
|
|
|
Compares the stored API key against the current one and updates if stale.
|
|
"""
|
|
ltype = provider_type.lower()
|
|
print(f"Checking {provider_type} provider...")
|
|
|
|
resp = http.get(
|
|
f"{base_url}/api/system/settings",
|
|
headers={"X-API-KEY": api_key},
|
|
timeout=30,
|
|
)
|
|
resp.raise_for_status()
|
|
settings = resp.json()
|
|
|
|
use_flag = settings.get("general", {}).get(f"use_{ltype}", False)
|
|
existing_key = settings.get(ltype, {}).get("apikey", "")
|
|
|
|
provider_api_key = read_api_key_xml(
|
|
f"{provider_config['dataDir']}/config.xml"
|
|
)
|
|
bind_address = provider_config.get("bindAddress", "127.0.0.1")
|
|
|
|
if use_flag and existing_key == provider_api_key:
|
|
print(f"{provider_type} provider already correct, skipping")
|
|
return
|
|
|
|
action = "Updating" if use_flag else "Adding"
|
|
print(f"{action} {provider_type} provider...")
|
|
|
|
resp = http.post(
|
|
f"{base_url}/api/system/settings",
|
|
headers={"X-API-KEY": api_key},
|
|
data={
|
|
f"settings-general-use_{ltype}": "true",
|
|
f"settings-{ltype}-ip": bind_address,
|
|
f"settings-{ltype}-port": str(provider_config["port"]),
|
|
f"settings-{ltype}-apikey": provider_api_key,
|
|
f"settings-{ltype}-ssl": "false",
|
|
f"settings-{ltype}-base_url": "/",
|
|
},
|
|
timeout=30,
|
|
)
|
|
resp.raise_for_status()
|
|
print(f"{provider_type} provider configured")
|
|
|
|
|
|
def main():
|
|
cfg = load_config()
|
|
data_dir = cfg["dataDir"]
|
|
bind_address = cfg.get("bindAddress", "127.0.0.1")
|
|
port = cfg["port"]
|
|
api_timeout = cfg["apiTimeout"]
|
|
|
|
config_yaml = f"{data_dir}/config/config.yaml"
|
|
if not os.path.isfile(config_yaml):
|
|
print(f"Config file {config_yaml} not found, skipping bazarr init")
|
|
return
|
|
|
|
api_key = read_api_key_yaml(config_yaml)
|
|
base_url = f"http://{bind_address}:{port}"
|
|
|
|
wait_for_api(
|
|
base_url,
|
|
api_key,
|
|
api_timeout,
|
|
"Bazarr",
|
|
header_name="X-API-KEY",
|
|
status_path="/api/system/status",
|
|
)
|
|
|
|
providers = cfg.get("providers", {})
|
|
if providers.get("sonarr", {}).get("enable"):
|
|
configure_provider(base_url, api_key, "Sonarr", providers["sonarr"])
|
|
if providers.get("radarr", {}).get("enable"):
|
|
configure_provider(base_url, api_key, "Radarr", providers["radarr"])
|
|
|
|
print("Bazarr init complete")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|