Skip to content

RXWatcher/silo-plugin-arr-proxy

Repository files navigation

Arr Proxy for Silo

silo.arrproxy is a Silo request router that forwards approved media requests to a single Arr Proxy backend which fronts both Radarr (movies) and Sonarr (TV) via path-routing (${BaseURL}/radarr/api/v3/... and ${BaseURL}/sonarr/api/v3/...). It consumes request events from silo.requests, submits them to the appropriate *arr API, polls for download/import progress, and publishes lifecycle status events back to the hub.

Category

Lives under Requests.

Install at most one router per installation — either this plugin or silo.arrouter (the rule-based multi-target alternative), not both.

Capabilities

Type ID Purpose
event_consumer.v1 request-router Consumes plugin.silo.requests.submitted and …cancelled, forwarding movies to Radarr and TV to Sonarr.
scheduled_task.v1 poll Periodic reconciliation against Radarr/Sonarr for queued, downloading, imported, cancelled, and stale-failed states.
http_routes.v1 admin Admin status board (SPA + JSON API) under /admin and /api/admin/*, with retry / cancel / force-fail for stuck rows.
request_router.v1 default Declares this plugin as the default forwarder for the single-backend Arr Proxy topology.

Dependencies

  • silo-plugin-requests — the upstream request intake plugin that publishes the submitted and cancelled events this plugin consumes. The consumer honours the optional target_plugin_id / target_provider_plugin_id fields on incoming payloads so multiple routers can coexist without double-routing.
  • Silo plugin host — provides the gRPC runtime, event hub, and admin-route proxying that this plugin plugs into via continuum-plugin-sdk.
  • Postgres — a dedicated arrproxy schema persists the request rows, external IDs, status, and the plugin-owned operational config (everything except database_url).

Host: ContinuumApp/silo.

External services

  • Arr Proxy backend — a single HTTP service that exposes Radarr at /radarr/... and Sonarr at /sonarr/... behind one base URL and one API key.
  • Radarr and Sonarr, reached only through the Arr Proxy. This plugin never contacts the underlying *arr services directly.

Request flow

  1. silo.requests publishes plugin.silo.requests.submitted after an approval.
  2. The event consumer validates the payload (TMDB ID, media type) and inserts a queued row in the local store.
  3. Based on mediaType:
    • movie — resolves Radarr defaults (root folder, quality profile), calls POST /radarr/api/v3/movie via the Arr Proxy with searchForMovie: true, then marks the row submitted and emits plugin.silo.arrproxy.submitted.
    • tv — looks up the series by TMDB ID through Sonarr, resolves defaults (root folder, quality + language profile), calls POST /sonarr/api/v3/series with monitor: all and searchForMissingEpisodes: true, then emits submitted.
    • Conflicts (409 already-exists) are treated as success and reconciled by the next poll tick.
  4. The poll loop walks rows in submitted/downloading every poll_interval_seconds (default 30s, clamped 10–600s), calling GET movie/{id}, GET series/{id}, and the relevant queue endpoint. It publishes downloading once an item appears in the *arr queue, and imported when Radarr reports hasFile or Sonarr reports statistics.percentOfEpisodes >= 100.
  5. Rows that stay non-terminal past stale_after_hours (default 72h) are marked failed with reason "stuck past stale window" and a failed event is emitted.
  6. On plugin.silo.requests.cancelled, the consumer deletes the matching movie/series in Radarr/Sonarr (if it was tracked) and marks the row cancelled.

Configuration

Only database_url is declared in the manifest's global_config_schema and set via the Silo host's plugin install form. Everything else is owned by the plugin and edited through the admin UI (persisted in the arrproxy.app_config table).

Key Source Required Description
database_url host yes Postgres DSN for the dedicated arrproxy schema. Pool capped at min 16 conns; override via ?pool_max_conns=N.
base_url admin UI yes Arr Proxy backend URL. Radarr and Sonarr URLs are derived as ${base_url}/radarr and ${base_url}/sonarr.
api_key admin UI yes Shared API key accepted by the Arr Proxy backend for both Radarr and Sonarr.
movies_path admin UI no Radarr root folder. Blank → first root folder returned by Radarr.
tv_path admin UI no Sonarr root folder. Blank → first root folder returned by Sonarr.
quality_profile_id admin UI no Quality profile applied to both Radarr and Sonarr. Blank → first profile returned by the service.
language_profile_id admin UI no Sonarr language profile. Blank → first profile.
polling.interval_seconds admin UI no Poll cadence in seconds. Default 30, clamped to [10, 600].
polling.stale_after_hours admin UI no Hours after which non-terminal rows are marked failed. Default 72. <= 0 disables staleness.

Database bootstrap:

CREATE ROLE plugin_arrproxy WITH LOGIN PASSWORD '<chosen>';
CREATE SCHEMA arrproxy AUTHORIZATION plugin_arrproxy;
GRANT CONNECT ON DATABASE silo TO plugin_arrproxy;

Schema migrations run automatically at startup.

Event subscriptions

  • plugin.silo.requests.submitted
  • plugin.silo.requests.cancelled

The consumer ignores any event whose target_plugin_id / target_provider_plugin_id field is set to another router's ID, so this plugin coexists safely with siblings on the same hub.

Event publications

All events are published under the plugin's own namespace (plugin.silo.arrproxy.<name>):

  • submitted — request accepted by Radarr/Sonarr (or already existed).
  • downloading — first time the item appears in an *arr download queue.
  • imported — Radarr hasFile, or Sonarr percentOfEpisodes >= 100.
  • failed — submission error, stale-window expiry, or operator force-fail. Payload includes an error string with the reason.
  • cancelled — operator cancellation, upstream request cancellation, or the item being removed from Radarr/Sonarr externally.
  • unrouted — reserved for events that did not match any media path (the consumer drops invalid payloads with a warning today).

Every payload carries requestId (and request_id) so downstream consumers can correlate against the originating request.

Detailed docs

Build and release

make build   # builds the SPA via pnpm, then the Go binary
make test    # runs `go test ./...`

CI builds linux-amd64 binaries on push to main via the reusable workflow in RXWatcher/silo-plugin-repository and publishes them to the catalog at ./binaries/.

About

Continuum plugin: arrproxy

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors