# arr-releases A Dynacat ([Panonim/dynacat](https://github.com/Panonim/dynacat)) calendar widget that aggregates upcoming releases from [Sonarr](https://sonarr.tv), [Radarr](https://radarr.video) and [Lidarr](https://lidarr.audio). - Always renders exactly 6 weeks (42 cells) with prev/next month spillover - One coloured dot per service on each day that has a release - Click any day with a dot to open a popover with full release details and cover art - Smooth slide+fade animation on month navigation - **Works with any subset of the three services configured** — set `SONARR_KEY` only and the widget will silently skip Radarr and Lidarr ![Calendar widget example](docs/screenshot.png) *(add a screenshot if you have one — see [How to add a screenshot](#how-to-add-a-screenshot))* ## Quick install (TL;DR) ```bash # 1. Copy the widget definition cp arr-calendar.yml /appdata/dynacat/config/ # 2. Copy the client JS cp arr-calendar.js /appdata/dynacat/assets/ # 3. Add this line to /appdata/dynacat/config/dynacat.yml under document.head: # # 4. Restart the Dynacat container (the Go template cache is in-memory; YML # edits do NOT recompile until the container restarts) docker restart dynacat ``` Then `$include: arr-calendar.yml` in any page column and set at least one of `SONARR_KEY`, `RADARR_KEY`, `LIDARR_KEY` as a container env var. ## What the widget does Each cell of the calendar corresponds to one day. On a day that has at least one release, the widget shows a small coloured dot: - 🔵 Blue dot — Sonarr episode - 🟢 Green dot — Radarr movie - 🟣 Purple dot — Lidarr album Multiple services on the same day produce a row of dots. Click the day to open a popover listing every release on that date, with cover art, the release type (Release / Digital / In Cinemas / Physical for Radarr; `S##E## — episode title` for Sonarr; artist name for Lidarr) and a link back to the *arr web UI. ## Full install ### 1. Environment variables The widget reads three API keys from the container environment. **Set at least one** — all three are optional: | Env var | Purpose | Required? | | ------------- | ------------------------------ | --------- | | `SONARR_KEY` | Sonarr API key | optional | | `RADARR_KEY` | Radarr API key | optional | | `LIDARR_KEY` | Lidarr API key | optional | | `BASE_HOST` | The shared part of the *arr URLs (e.g. `t3du.com`). The widget builds Sonarr/Radarr/Lidarr URLs as `https://.${BASE_HOST}`. | required | Get an API key from each *arr under **Settings → General → API Key**. If you set the env var on the Dynacat container itself, no per-service configuration is needed — defaults to `https://sonarr.${BASE_HOST}`, `https://radarr.${BASE_HOST}`, `https://lidarr.${BASE_HOST}`. To use a different URL for a service, set the corresponding `*-url` option in your `$include` of `arr-calendar.yml` (see [Customising URLs](#customising-urls) below). ### 2. File layout ``` /appdata/dynacat/ config/ dynacat.yml # main config (edit to add the script tag) arr-calendar.yml # <-- this repo, copy here assets/ arr-calendar.js # <-- this repo, copy here global.css # (untouched; widget CSS is inlined in the YML) ``` ### 3. Edit `dynacat.yml` Add the script tag under `document.head`: ```yaml server: assets-path: /app/assets document: head: | ``` Then `$include` the widget in a page column (any column size — it adapts): ```yaml pages: - name: Home columns: - size: small widgets: - $include: arr-calendar.yml ``` ### 4. Restart the container **YML edits do not recompile the Go template on the fly.** Glance/Dynacat parses each `custom-api` template once on first render and caches the compiled `*template.Template` in memory. File mtime updates, `touch` and edits to the file do not invalidate the cache. After every edit to `arr-calendar.yml`, restart the container: ```bash docker restart dynacat ``` (or via your container manager — Dockhand, Unraid UI, etc.). The error message you'll get if you forget is the dreaded `Config has errors: custom-api widget: parsing template: ...` — see [Troubleshooting](#troubleshooting) below. ### 5. Hard-refresh the browser `Ctrl+Shift+R` (or `Cmd+Shift+R` on macOS) to bypass the browser cache. ## Customising All options live in the top of `arr-calendar.yml` under `options:`: ```yaml - type: custom-api options: sonarr-url: https://sonarr.example.com sonarr-key: ${SONARR_KEY} radarr-url: https://radarr.example.com radarr-key: ${RADARR_KEY} lidarr-url: https://lidarr.example.com lidarr-key: ${LIDARR_KEY} exclude-physical: true past-days: 90 future-days: 90 ``` | Option | Default | Notes | | ---------------- | ------------------- | ----- | | `sonarr-url` | `https://sonarr.${BASE_HOST}` | Override if your Sonarr is on a different host | | `sonarr-key` | `${SONARR_KEY}` | Set the env var instead of hard-coding | | `radarr-url` | `https://radarr.${BASE_HOST}` | | | `radarr-key` | `${RADARR_KEY}` | | | `lidarr-url` | `https://lidarr.${BASE_HOST}` | | | `lidarr-key` | `${LIDARR_KEY}` | | | `exclude-physical` | `true` | When `true`, Radarr movies whose display date is a physical release are hidden. Set to `false` to show them. | | `past-days` | `90` | How many days into the past to fetch (for the "this month" view, so the first week of the month shows releases from the previous month) | | `future-days` | `90` | How many days into the future to fetch (should be ≥ 60 to cover the full ~2-month view) | If you want to keep the defaults but override the URLs, the cleanest pattern is to copy the widget's options block into your main `dynacat.yml` and `$include` the rest, or to edit `arr-calendar.yml` directly. ## Customising URLs The default URLs use the `BASE_HOST` env var and assume Sonarr/Radarr/Lidarr are reachable as subdomains of that host. To use a different host for one of the services, override the corresponding `*-url` option in your `$include`. The YML supports Go template strings, so you can interpolate env vars: ```yaml - type: custom-api options: sonarr-url: https://sonarr.${BASE_HOST} sonarr-key: ${SONARR_KEY} radarr-url: http://192.168.1.50:7878 # absolute override radarr-key: ${RADARR_KEY} lidarr-key: ${LIDARR_KEY} # omitted URL → defaults to https://lidarr.${BASE_HOST} ``` Note: at least one of the three `(url, key)` pairs must be set per service for the service to be queried. An empty `*-key` skips the service entirely — see [Partial-services support](#partial-services-support). ## Partial-services support This is the headline feature. The widget keeps working with **any subset of the three services configured**: - Set only `SONARR_KEY` → calendar shows only Sonarr releases (blue dots only) - Set all three → all three colours of dots appear - Set none → soft empty-state message: *"Configure at least one of SONARR_KEY, RADARR_KEY, or LIDARR_KEY in the container environment to show releases."* A service is "configured" if both its URL and API key are non-empty. If either is empty, that service's API is silently skipped — no request goes out, no error widget appears, and the widget continues to render with the remaining services. If a service is **configured** but **unreachable** (network down, service dead, 401 from a wrong key), the same logic applies: that service is skipped (the 5s HTTP timeout will trip), and the other services continue to render. The status of each service is exposed to the client JS in a hidden `