#!/usr/bin/env python3 """ Add every missing Criterion film to Radarr as a monitored movie (no search yet). Reads the JSON written by criterion_crossref.py. Films without a TMDB ID are listed and skipped. Films Radarr already has are counted as skipped. Usage: export RADARR_URL="http://radarr.local:7878" export RADARR_API_KEY="your-radarr-api-key" export RADARR_ROOT_PATH="/movies" # a root folder Radarr already knows export RADARR_QUALITY_PROFILE_ID="1" # Radarr → Settings → Profiles (id in the URL) python add_to_radarr.py [criterion_crossref.json] Requires only the Python standard library. """ import json, os, sys, urllib.error, urllib.request def env(name): value = os.environ.get(name, "") if not value: print(f"ERROR: {name} not set.", file=sys.stderr) sys.exit(1) return value BASE = env("RADARR_URL").rstrip("/") API_KEY = env("RADARR_API_KEY") ROOT_PATH = env("RADARR_ROOT_PATH") QUALITY_PROFILE_ID = int(env("RADARR_QUALITY_PROFILE_ID")) IN_PATH = sys.argv[1] if len(sys.argv) > 1 else "criterion_crossref.json" with open(IN_PATH) as f: data = json.load(f) missing = data["missing"] + [m["criterion"] for m in data["fuzzy"]] films_with_tmdb = [f for f in missing if f.get("tmdbId")] films_without_tmdb = [f for f in missing if not f.get("tmdbId")] print(f"Adding {len(films_with_tmdb)} films to Radarr (monitored)...", file=sys.stderr) if films_without_tmdb: print(f"Skipping {len(films_without_tmdb)} films without TMDB ID:", file=sys.stderr) for f in films_without_tmdb: print(f" {f['title']}", file=sys.stderr) added = 0 skipped = 0 errors = 0 for i, film in enumerate(films_with_tmdb, 1): payload = json.dumps({ "tmdbId": film["tmdbId"], "title": film["title"], "year": film.get("year") or 0, "rootFolderPath": ROOT_PATH, "qualityProfileId": QUALITY_PROFILE_ID, "monitored": True, "addOptions": { "searchForMovie": False } }).encode("utf-8") req = urllib.request.Request( f"{BASE}/api/v3/movie", data=payload, headers={ "X-Api-Key": API_KEY, "Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (criterion-crossref/1.0)", }, method="POST", ) try: with urllib.request.urlopen(req, timeout=30) as r: r.read() added += 1 except urllib.error.HTTPError as e: body = e.read().decode("utf-8", errors="replace") if "already" in body.lower() or e.code == 400: skipped += 1 else: errors += 1 print(f" Error adding {film['title']}: HTTP {e.code} - {body[:100]}", file=sys.stderr) except Exception as e: errors += 1 print(f" Error adding {film['title']}: {e}", file=sys.stderr) if i % 100 == 0: print(f" ...{i}/{len(films_with_tmdb)} (added={added}, skipped={skipped}, errors={errors})", file=sys.stderr) print(f"\nDone! Added: {added}, Skipped: {skipped}, Errors: {errors}", file=sys.stderr)