diff --git a/importarr/main.py b/importarr/main.py index 897a8d2..10e3ef2 100644 --- a/importarr/main.py +++ b/importarr/main.py @@ -41,6 +41,25 @@ class QueueItemActionRequest(BaseModel): action: str +class AppSettingsUpdate(BaseModel): + sab_url: str + sab_api_key: str | None = None + radarr_url: str | None = None + radarr_api_key: str | None = None + sonarr_url: str | None = None + sonarr_api_key: str | None = None + + +def load_ui_settings() -> None: + for key in ("sab_url", "sab_api_key", "radarr_url", "radarr_api_key", "sonarr_url", "sonarr_api_key"): + stored = state.get_app_state(key) + if stored is not None: + setattr(settings, key, stored or None) + + +load_ui_settings() + + def require_write_auth(authorization: Annotated[str | None, Header()] = None) -> None: if not settings.auth_token: return @@ -70,6 +89,11 @@ def status() -> dict[str, object]: "movies_root": str(settings.movies_root), "tv_root": str(settings.tv_root), "sab_url": settings.sab_url, + "sab_api_key_configured": bool(settings.sab_api_key), + "radarr_url": settings.radarr_url or "", + "radarr_api_key_configured": bool(settings.radarr_api_key), + "sonarr_url": settings.sonarr_url or "", + "sonarr_api_key_configured": bool(settings.sonarr_api_key), "auth_enabled": bool(settings.auth_token), "bind": f"{settings.bind_host}:{settings.bind_port}", "manual_batches": len(state.list_manual_batches(active_only=True)), @@ -80,6 +104,38 @@ def status() -> dict[str, object]: } +@app.get("/api/settings") +def get_ui_settings() -> dict[str, object]: + return { + "sab_url": settings.sab_url, + "sab_api_key_configured": bool(settings.sab_api_key), + "radarr_url": settings.radarr_url or "", + "radarr_api_key_configured": bool(settings.radarr_api_key), + "sonarr_url": settings.sonarr_url or "", + "sonarr_api_key_configured": bool(settings.sonarr_api_key), + } + + +@app.post("/api/settings") +def update_ui_settings(payload: AppSettingsUpdate, _: None = Depends(require_write_auth)) -> dict[str, object]: + sab_url = payload.sab_url.strip() + if not sab_url: + raise HTTPException(status_code=400, detail="SAB URL is required") + values = { + "sab_url": sab_url, + "sab_api_key": (payload.sab_api_key or "").strip(), + "radarr_url": (payload.radarr_url or "").strip(), + "radarr_api_key": (payload.radarr_api_key or "").strip(), + "sonarr_url": (payload.sonarr_url or "").strip(), + "sonarr_api_key": (payload.sonarr_api_key or "").strip(), + } + for key, value in values.items(): + state.set_app_state(key, value) + setattr(settings, key, value or None) + settings.sab_url = sab_url + return get_ui_settings() + + def control_status() -> dict[str, object]: mode = state.get_app_state("queue_mode", "running") or "running" current = state.get_app_state("current_job") diff --git a/importarr/static/importarr.css b/importarr/static/importarr.css index 2a4e44e..04fc320 100644 --- a/importarr/static/importarr.css +++ b/importarr/static/importarr.css @@ -1 +1 @@ -body{font-family:system-ui,sans-serif;margin:0;background:#111827;color:#e5e7eb}header,main{max-width:1100px;margin:auto;padding:1rem}.topbar{display:flex;justify-content:space-between;gap:1rem;align-items:center;background:#0f172a}.build{text-align:right}.build strong{font-size:1.2rem}.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(12rem,1fr));gap:1rem}.cards article,.panel{background:#1f2937;border-radius:.75rem;padding:1rem;margin-top:1rem}strong{display:block;font-size:2rem}span,small,dd{color:#9ca3af}table{width:100%;border-collapse:collapse;background:#1f2937;margin-top:1rem}th,td{padding:.6rem;border-bottom:1px solid #374151;text-align:left;vertical-align:top}input,button{padding:.6rem;border-radius:.4rem;border:1px solid #374151}button{background:#38bdf8;color:#082f49;font-weight:700;cursor:pointer}.danger{background:#f87171;color:#450a0a}.warn{background:#fbbf24;color:#451a03}.controls,.row-actions{display:flex;gap:.5rem;flex-wrap:wrap}.inline-form{display:flex;gap:.5rem;flex-wrap:wrap}.inline-form input[name=path]{min-width:min(100%,28rem);flex:1}.info{display:grid;grid-template-columns:10rem 1fr;gap:.4rem 1rem}.info dt{font-weight:700}.info dd{margin:0;overflow-wrap:anywhere}.state{background:#0f172a;border:1px solid #374151;border-radius:999px;padding:.15rem .5rem;display:inline-block}.section-title{display:flex;align-items:center;justify-content:space-between;gap:1rem}.job-group{margin-top:1.25rem}.job-group h3{display:flex;gap:.5rem;align-items:center}.job-group h3 span{font-size:.9rem;border:1px solid #374151;border-radius:999px;padding:.1rem .45rem}.file-name{font-size:1rem}.row-actions button{padding:.35rem .5rem}td small{display:block;overflow-wrap:anywhere} +body{font-family:system-ui,sans-serif;margin:0;background:#111827;color:#e5e7eb}header,main{max-width:1100px;margin:auto;padding:1rem}.topbar{display:flex;justify-content:space-between;gap:1rem;align-items:center;background:#0f172a}.build{text-align:right}.build strong{font-size:1.2rem}.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(12rem,1fr));gap:1rem}.cards article,.panel{background:#1f2937;border-radius:.75rem;padding:1rem;margin-top:1rem}strong{display:block;font-size:2rem}span,small,dd{color:#9ca3af}table{width:100%;border-collapse:collapse;background:#1f2937;margin-top:1rem}th,td{padding:.6rem;border-bottom:1px solid #374151;text-align:left;vertical-align:top}input,button{padding:.6rem;border-radius:.4rem;border:1px solid #374151}button{background:#38bdf8;color:#082f49;font-weight:700;cursor:pointer}.danger{background:#f87171;color:#450a0a}.warn{background:#fbbf24;color:#451a03}.controls,.row-actions{display:flex;gap:.5rem;flex-wrap:wrap}.inline-form{display:flex;gap:.5rem;flex-wrap:wrap}.inline-form input[name=path]{min-width:min(100%,28rem);flex:1}.info{display:grid;grid-template-columns:10rem 1fr;gap:.4rem 1rem}.info dt{font-weight:700}.info dd{margin:0;overflow-wrap:anywhere}.state{background:#0f172a;border:1px solid #374151;border-radius:999px;padding:.15rem .5rem;display:inline-block}.section-title{display:flex;align-items:center;justify-content:space-between;gap:1rem}.job-group{margin-top:1.25rem}.job-group h3{display:flex;gap:.5rem;align-items:center}.job-group h3 span{font-size:.9rem;border:1px solid #374151;border-radius:999px;padding:.1rem .45rem}.file-name{font-size:1rem}.row-actions button{padding:.35rem .5rem}td small{display:block;overflow-wrap:anywhere}dialog{background:#1f2937;color:#e5e7eb;border:1px solid #374151;border-radius:.75rem;max-width:min(42rem,90vw)}dialog::backdrop{background:#0009}fieldset{border:1px solid #374151;border-radius:.5rem;margin:1rem 0;padding:1rem}label{display:grid;gap:.35rem;margin:.75rem 0}.hint{color:#9ca3af} diff --git a/importarr/templates/index.html b/importarr/templates/index.html index 4076bd6..e9bebba 100644 --- a/importarr/templates/index.html +++ b/importarr/templates/index.html @@ -9,7 +9,7 @@
Manual SABnzbd imports, safely gated by SAB completion.