A one-page family front for Seerr, Radarr and Sonarr: search, request, download queue
  • Go 93.2%
  • HTML 4.1%
  • CSS 2.3%
  • Dockerfile 0.4%
Find a file
agent 2dd7af5ad2
All checks were successful
ci / test (push) Successful in 1m32s
ci / image (push) Successful in 1m41s
ci on Forgejo Actions; the image is sarak.sh/arr/zeleerr
2026-10-21 19:54:41 +02:00
.forgejo/workflows ci on Forgejo Actions; the image is sarak.sh/arr/zeleerr 2026-10-21 19:54:41 +02:00
api zelirr: search, request and the download queue for the family 2026-10-19 12:42:26 +02:00
cmd torznab-years: the series year in release titles for Sonarr 2026-10-19 18:19:01 +02:00
internal torznab-years: the series year in release titles for Sonarr 2026-10-19 18:19:01 +02:00
.dockerignore zelirr: search, request and the download queue for the family 2026-10-19 12:42:26 +02:00
.gitignore Rename to zeleerr 2026-10-19 14:55:36 +02:00
Dockerfile seerr-gate: Seerr's pages for people the proxy has signed in 2026-10-19 16:46:31 +02:00
go.mod Queue with what is still searched for, in the browser's language 2026-10-19 15:15:54 +02:00
go.sum zelirr: search, request and the download queue for the family 2026-10-19 12:42:26 +02:00
LICENSE zelirr: search, request and the download queue for the family 2026-10-19 12:42:26 +02:00
README.md ci on Forgejo Actions; the image is sarak.sh/arr/zeleerr 2026-10-21 19:54:41 +02:00

zeleerr

A one-page front for the family in front of Seerr, Radarr and Sonarr: search for a movie or a series, request it, and watch the download queue. It has no sign-in of its own and trusts a header set by the authenticating proxy in front of it.

How it works

  • The signed-in user's email comes from a request header, X-Auth-Request-Email by default, as oauth2-proxy sets it with --set-xauthrequest. A request without it gets 401.
  • Each email is matched to a Seerr user. If Seerr has none, zeleerr creates a local one and gives it the permissions in ZELEERR_PERMISSIONS; a user Seerr already has keeps its own.
  • A request goes to Seerr on behalf of that user. A series is requested with every season.
  • The queue is read from Radarr and Sonarr directly. A series' episodes are folded into one entry per season. Below the downloads come the requests Seerr is still processing with nothing downloading yet.
  • The page speaks Russian, or C, its untranslated English, to anyone asking for another language; it names movies and series in that language where TMDB has the name. The language is the one picked on the page, kept in a cookie, else the locale claim of the person's OpenID Connect profile, else the browser's Accept-Language. Each movie and series links to its page in Seerr.
  • With ZELEERR_OIDC_ISSUER set, the page shows the person's name and picture from the provider's userinfo, asked with the access token the proxy passes on.

Run it

SEERR_URL=http://seerr:5055 SEERR_API_KEY=... \
RADARR_URL=http://radarr:7878 RADARR_API_KEY=... \
SONARR_URL=http://sonarr:8989 SONARR_API_KEY=... \
go run ./cmd/zeleerr

The image is sarak.sh/arr/zeleerr, built by .forgejo/workflows/ci.yaml on Forgejo Actions at https://forge.sarak.sh/arr/zeleerr: every push to main is tagged main-<sha>-<unix time>, and a vX.Y.Z tag X.Y.Z.

Configuration

Variable Default Meaning
SEERR_URL, SEERR_API_KEY Seerr, without /api/v1
SEERR_PUBLIC_URL SEERR_URL Seerr as the browser reaches it, for links
RADARR_URL, RADARR_API_KEY Radarr
SONARR_URL, SONARR_API_KEY Sonarr
ZELEERR_ADDR :8080 listen address
ZELEERR_USER_HEADER X-Auth-Request-Email header carrying the user's email
ZELEERR_OIDC_ISSUER OpenID Connect issuer for names, pictures and locales; without it people are shown by email
ZELEERR_TOKEN_HEADER X-Forwarded-Access-Token header carrying the access token, as oauth2-proxy sets it with --pass-access-token
ZELEERR_PERMISSIONS 160 Seerr permission bits for a user zeleerr creates: REQUEST and AUTO_APPROVE

/healthz answers without the header.

seerr-gate

The image also carries /seerr-gate, a reverse proxy that serves Seerr's own pages to people the authenticating proxy has signed in, without a sign-in of Seerr's. It keeps a Seerr session for each email and puts it on their requests; the browser never holds one. It signs in as the local Seerr user of that email with the password HMAC-SHA256(GATE_KEY, email), and sets that password through the API, creating the user if Seerr has none, when the sign-in fails. Seerr wants the current password to change the API key owner's own, so a new GATE_KEY needs that user's password reset by hand.

Variable Default Meaning
SEERR_URL, SEERR_API_KEY Seerr, without /api/v1, and a key with MANAGE_USERS
GATE_KEY key the passwords derive from
GATE_ADDR :8081 listen address
GATE_USER_HEADER X-Forwarded-Email header carrying the user's email
GATE_PERMISSIONS 160 Seerr permission bits for a user the gate creates

/_gate/healthz answers without the header.

torznab-years

The image also carries /torznab-years, a reverse proxy to Prowlarr for Sonarr. TheTVDB, and Sonarr with it, titles a series "Dark Matter (2024)" when another series shares the name, while RuTracker titles its releases "Темная материя / Dark Matter / S2E1-7 of 10 [2026, ...]": no year by the name, and the year in the tags is the season's. Sonarr rejects such releases as an unknown series. In Torznab search answers the proxy replaces the name with Sonarr's title, taking, of the series of that name, the one with the latest year not after the year in the tags. Series titled without a year are left alone, and everything but search answers passes through. Point Sonarr's indexers at it through the Prowlarr Server URL of the Sonarr application in Prowlarr.

Variable Default Meaning
PROWLARR_URL Prowlarr
SONARR_URL, SONARR_API_KEY Sonarr, for its series
YEARS_ADDR :9696 listen address
YEARS_REFRESH 10m how often the series are fetched again

/_years/healthz answers itself.

Development

The API clients in internal/{seerr,radarr,sonarr} are generated by oapi-codegen from the specs in api/:

go generate ./...
go test ./...

License

MIT