fa442b1fef
- Self-contained YML: all CSS inlined (no global.css edit needed) - Vanilla JS client with MutationObserver bootstrap - Slide+fade month navigation via rAF (works in this Dynacat build) - Popover portaled to <body> to escape backdrop-filter containing blocks - Per-service enabled flag, unconfigured services silently skipped - Status <ul> exposes per-service configured/ok flags for client hints
506 lines
19 KiB
YAML
506 lines
19 KiB
YAML
- type: custom-api
|
|
title: Arr Calendar
|
|
hide-header: true
|
|
update-interval: 10m
|
|
options:
|
|
radarr-url: https://radarr.${BASE_HOST}
|
|
radarr-key: ${RADARR_KEY}
|
|
sonarr-url: https://sonarr.${BASE_HOST}
|
|
sonarr-key: ${SONARR_KEY}
|
|
lidarr-url: https://lidarr.${BASE_HOST}
|
|
lidarr-key: ${LIDARR_KEY}
|
|
exclude-physical: true
|
|
past-days: 90
|
|
future-days: 90
|
|
template: |
|
|
{{- /* === *arr Calendar Widget — self-contained === */}}
|
|
{{- /* CSS is inlined below (works because Dynacat CSP allows style-src */}}
|
|
{{- /* 'self' 'unsafe-inline'). JS lives in /assets/arr-calendar.js and */}}
|
|
{{- /* must be wired once in dynacat.yml > document.head: */}}
|
|
{{- /* <script src="/assets/arr-calendar.js" defer></script> */}}
|
|
|
|
<style>
|
|
.arr-calendar-widget {
|
|
--arr-sonarr: #3b82f6;
|
|
--arr-radarr: #22c55e;
|
|
--arr-lidarr: #a855f7;
|
|
position: relative;
|
|
font-family: inherit;
|
|
}
|
|
.arr-calendar-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 0.4rem;
|
|
margin-bottom: 0.6rem;
|
|
min-height: 2.8rem;
|
|
}
|
|
.arr-cal-header-left {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
min-width: 0;
|
|
height: 2.8rem;
|
|
}
|
|
.arr-cal-month {
|
|
font-size: var(--font-size-h2, 1.6rem);
|
|
font-weight: 600;
|
|
color: var(--color-text-highlight);
|
|
line-height: 1;
|
|
}
|
|
.arr-cal-year {
|
|
font-size: var(--font-size-h3, 1.5rem);
|
|
font-weight: 500;
|
|
color: var(--color-text-highlight);
|
|
line-height: 1;
|
|
}
|
|
.arr-cal-undo {
|
|
background: none;
|
|
border: none;
|
|
position: relative;
|
|
cursor: pointer;
|
|
width: 2.8rem;
|
|
height: 2.8rem;
|
|
color: var(--color-text-base);
|
|
z-index: 1;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 0;
|
|
margin-left: 0.2rem;
|
|
border-radius: var(--border-radius, 5px);
|
|
transition: color 0.15s ease;
|
|
}
|
|
.arr-cal-undo[hidden] { display: none; }
|
|
.arr-cal-undo svg { width: 2rem; height: 2rem; }
|
|
.arr-cal-undo::before {
|
|
content: '';
|
|
position: absolute;
|
|
inset: -0.2rem;
|
|
border-radius: var(--border-radius, 5px);
|
|
background-color: var(--color-text-subdue);
|
|
opacity: 0;
|
|
transition: opacity 0.2s;
|
|
z-index: -1;
|
|
}
|
|
.arr-cal-undo:hover { color: var(--color-text-highlight); }
|
|
.arr-cal-undo:hover::before { opacity: 0.4; }
|
|
.arr-cal-header-right {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.7rem;
|
|
}
|
|
.arr-cal-monthnum {
|
|
font-size: var(--font-size-h3, 1.5rem);
|
|
font-weight: 500;
|
|
color: var(--color-text-highlight);
|
|
line-height: 1;
|
|
margin-top: 0.1rem;
|
|
min-width: 1.4rem;
|
|
text-align: center;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.arr-cal-nav {
|
|
background: none;
|
|
border: none;
|
|
position: relative;
|
|
cursor: pointer;
|
|
width: 2.8rem;
|
|
height: 2.8rem;
|
|
color: var(--color-text-base);
|
|
z-index: 1;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 0;
|
|
border-radius: var(--border-radius, 5px);
|
|
transition: color 0.15s ease;
|
|
}
|
|
.arr-cal-nav svg { width: 2rem; height: 2rem; }
|
|
.arr-cal-nav::before {
|
|
content: '';
|
|
position: absolute;
|
|
inset: -0.2rem;
|
|
border-radius: var(--border-radius, 5px);
|
|
background-color: var(--color-text-subdue);
|
|
opacity: 0;
|
|
transition: opacity 0.2s;
|
|
z-index: -1;
|
|
}
|
|
.arr-cal-nav:hover { color: var(--color-text-highlight); }
|
|
.arr-cal-nav:hover::before { opacity: 0.4; }
|
|
.arr-calendar-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
gap: 4px;
|
|
text-align: center;
|
|
}
|
|
.arr-cal-weekday {
|
|
font-size: var(--font-size-h6, 1.1rem);
|
|
font-weight: 500;
|
|
text-transform: uppercase;
|
|
color: var(--color-text-subdue);
|
|
padding: 0.3rem 0;
|
|
line-height: 1.4;
|
|
}
|
|
.arr-cal-cell {
|
|
position: relative;
|
|
padding: 0.4rem 0;
|
|
color: var(--color-text-base);
|
|
border-radius: var(--border-radius, 5px);
|
|
background: none;
|
|
border: none;
|
|
font: inherit;
|
|
font-size: var(--font-size-base, 1.3rem);
|
|
cursor: pointer;
|
|
height: 3.2rem;
|
|
min-height: 3.2rem;
|
|
max-height: 3.2rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
transition: color 0.15s ease, background-color 0.15s ease;
|
|
}
|
|
.arr-cal-cell:hover:not(.arr-cal-outside) {
|
|
background-color: var(--color-widget-background-highlight);
|
|
}
|
|
.arr-cal-outside {
|
|
color: var(--color-text-subdue);
|
|
cursor: default;
|
|
pointer-events: none;
|
|
}
|
|
.arr-cal-today {
|
|
background-color: var(--color-popover-border);
|
|
color: var(--color-text-highlight);
|
|
}
|
|
.arr-cal-has-releases { font-weight: 600; }
|
|
.arr-cal-num {
|
|
font-size: inherit;
|
|
font-weight: inherit;
|
|
line-height: 1;
|
|
padding-bottom: 0.4rem;
|
|
}
|
|
.arr-cal-dots {
|
|
position: absolute;
|
|
bottom: 0.15rem;
|
|
left: 0;
|
|
right: 0;
|
|
display: inline-flex;
|
|
gap: 0.2rem;
|
|
align-items: center;
|
|
justify-content: center;
|
|
pointer-events: none;
|
|
line-height: 0;
|
|
}
|
|
.arr-dot {
|
|
display: inline-block;
|
|
width: 0.45rem;
|
|
height: 0.45rem;
|
|
border-radius: 50%;
|
|
flex-shrink: 0;
|
|
}
|
|
.arr-dot-sonarr { background: var(--arr-sonarr); }
|
|
.arr-dot-radarr { background: var(--arr-radarr); }
|
|
.arr-dot-lidarr { background: var(--arr-lidarr); }
|
|
.arr-calendar-popover {
|
|
position: fixed;
|
|
z-index: 1000;
|
|
top: 0;
|
|
left: 0;
|
|
width: max-content;
|
|
max-width: min(360px, calc(100vw - 2rem));
|
|
min-width: 260px;
|
|
max-height: 60vh;
|
|
overflow-y: auto;
|
|
background: var(--color-popover-background);
|
|
border: 1px solid var(--color-popover-border);
|
|
border-radius: var(--border-radius, 5px);
|
|
padding: 0.75rem 0.9rem;
|
|
box-shadow: 0 15px 20px -10px hsla(var(--bghs), calc(var(--bgl) * 0.2), 0.5);
|
|
color: var(--color-text-paragraph);
|
|
}
|
|
.arr-popover-header {
|
|
font-weight: 600;
|
|
font-size: var(--font-size-h3, 1.5rem);
|
|
margin-bottom: 0.6rem;
|
|
padding-bottom: 0.4rem;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
}
|
|
.arr-popover-empty {
|
|
font-size: var(--font-size-base, 1.3rem);
|
|
opacity: 0.7;
|
|
padding: 0.4rem 0;
|
|
}
|
|
.arr-popover-list {
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.55rem;
|
|
}
|
|
.arr-popover-item {
|
|
display: flex;
|
|
gap: 0.6rem;
|
|
align-items: center;
|
|
padding: 0.3rem;
|
|
border-radius: 0.4rem;
|
|
background: rgba(255, 255, 255, 0.04);
|
|
border-left: 3px solid transparent;
|
|
}
|
|
.arr-popover-item.arr-svc-sonarr { border-left-color: var(--arr-sonarr); }
|
|
.arr-popover-item.arr-svc-radarr { border-left-color: var(--arr-radarr); }
|
|
.arr-popover-item.arr-svc-lidarr { border-left-color: var(--arr-lidarr); }
|
|
.arr-popover-cover {
|
|
width: 36px;
|
|
height: 54px;
|
|
object-fit: cover;
|
|
border-radius: 0.25rem;
|
|
flex-shrink: 0;
|
|
background: rgba(255, 255, 255, 0.05);
|
|
opacity: 1 !important;
|
|
}
|
|
.arr-popover-text { flex: 1; min-width: 0; }
|
|
.arr-popover-title {
|
|
display: block;
|
|
font-weight: 500;
|
|
font-size: var(--font-size-h4, 1.4rem);
|
|
text-decoration: none;
|
|
color: inherit;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.arr-popover-title:hover { color: var(--color-primary, #a78bfa); }
|
|
.arr-popover-sub {
|
|
font-size: var(--font-size-h6, 1.1rem);
|
|
opacity: 0.8;
|
|
margin-top: 0.15rem;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.4rem;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.arr-popover-badge {
|
|
display: inline-block;
|
|
padding: 0.1rem 0.5rem;
|
|
border-radius: 0.3rem;
|
|
font-size: 0.9rem;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
color: #fff;
|
|
flex-shrink: 0;
|
|
}
|
|
.arr-svc-badge-sonarr { background: var(--arr-sonarr); }
|
|
.arr-svc-badge-radarr { background: var(--arr-radarr); }
|
|
.arr-svc-badge-lidarr { background: var(--arr-lidarr); }
|
|
.arr-release-data,
|
|
.arr-calendar-status { display: none !important; }
|
|
.arr-calendar-empty {
|
|
font-size: var(--font-size-base, 1.3rem);
|
|
color: var(--color-text-subdue);
|
|
padding: 1.2rem 0.4rem;
|
|
text-align: center;
|
|
line-height: 1.4;
|
|
}
|
|
.arr-calendar-empty code {
|
|
font-size: 0.95em;
|
|
padding: 0.05rem 0.3rem;
|
|
background: var(--color-widget-background-highlight, rgba(255,255,255,0.05));
|
|
border-radius: 0.25rem;
|
|
}
|
|
</style>
|
|
|
|
{{ $radarrUrl := .Options.StringOr "radarr-url" "" }}
|
|
{{ $sonarrUrl := .Options.StringOr "sonarr-url" "" }}
|
|
{{ $lidarrUrl := .Options.StringOr "lidarr-url" "" }}
|
|
{{ $radarrKey := .Options.StringOr "radarr-key" "" }}
|
|
{{ $sonarrKey := .Options.StringOr "sonarr-key" "" }}
|
|
{{ $lidarrKey := .Options.StringOr "lidarr-key" "" }}
|
|
{{ $excludePhysical := .Options.BoolOr "exclude-physical" false }}
|
|
{{ $pastDays := .Options.IntOr "past-days" 90 }}
|
|
{{ $futureDays := .Options.IntOr "future-days" 90 }}
|
|
|
|
{{ $pastH := mul $pastDays 24 }}
|
|
{{ $futureH := mul $futureDays 24 }}
|
|
|
|
{{ $start := offsetNow (printf "-%dh" $pastH) | formatTime "2006-01-02T15:04:05" }}
|
|
{{ $end := offsetNow (printf "+%dh" $futureH) | formatTime "2006-01-02T15:04:05" }}
|
|
{{ $nowIso := now | formatTime "2006-01-02T15:04:05" }}
|
|
|
|
{{- /* A service is "configured" if BOTH the URL and the API key are set.
|
|
Unconfigured services are silently skipped — no request, no error.
|
|
|
|
We declare $xxxData at the OUTER scope with an empty URL, which
|
|
Glance's getResponse() handles by returning a zero-valued response
|
|
with Response.StatusCode = 0 (see fetchCustomAPIResponse in
|
|
widget-custom-api.go: empty URL short-circuits to a no-op). The
|
|
outer-scope declaration is required because Go text/template
|
|
variables declared with `:=` inside an if-block are scoped to that
|
|
block — referencing them outside the block yields "undefined
|
|
variable". The if-block then reassigns the outer variable with `=`
|
|
when the service is enabled. */}}
|
|
{{ $radarrEnabled := and (ne $radarrUrl "") (ne $radarrKey "") }}
|
|
{{ $sonarrEnabled := and (ne $sonarrUrl "") (ne $sonarrKey "") }}
|
|
{{ $lidarrEnabled := and (ne $lidarrUrl "") (ne $lidarrKey "") }}
|
|
|
|
{{- $radarrData := newRequest "" | getResponse }}
|
|
{{- $sonarrData := newRequest "" | getResponse }}
|
|
{{- $lidarrData := newRequest "" | getResponse }}
|
|
|
|
{{- if $radarrEnabled }}
|
|
{{ $radarrReq := printf "%s/api/v3/calendar?start=%s&end=%s" $radarrUrl $start $end }}
|
|
{{- $radarrData = newRequest $radarrReq
|
|
| withHeader "Accept" "application/json"
|
|
| withHeader "X-Api-Key" $radarrKey
|
|
| getResponse }}
|
|
{{- end }}
|
|
|
|
{{- if $sonarrEnabled }}
|
|
{{ $sonarrReq := printf "%s/api/v3/calendar?includeSeries=true&start=%s&end=%s" $sonarrUrl $start $end }}
|
|
{{- $sonarrData = newRequest $sonarrReq
|
|
| withHeader "Accept" "application/json"
|
|
| withHeader "X-Api-Key" $sonarrKey
|
|
| getResponse }}
|
|
{{- end }}
|
|
|
|
{{- if $lidarrEnabled }}
|
|
{{ $lidarrReq := printf "%s/api/v1/calendar?start=%s&end=%s" $lidarrUrl $start $end }}
|
|
{{- $lidarrData = newRequest $lidarrReq
|
|
| withHeader "Accept" "application/json"
|
|
| withHeader "X-Api-Key" $lidarrKey
|
|
| getResponse }}
|
|
{{- end }}
|
|
|
|
{{ $anyConfigured := or $radarrEnabled $sonarrEnabled $lidarrEnabled }}
|
|
|
|
<div class="arr-calendar-widget" data-today="{{ now | formatTime "2006-01-02" }}">
|
|
<div class="arr-calendar-header">
|
|
<div class="arr-cal-header-left">
|
|
<div class="arr-cal-month" data-arr-month></div>
|
|
<div class="arr-cal-year" data-arr-year></div>
|
|
<button class="arr-cal-undo" type="button" data-arr-undo title="Back to current month" hidden>
|
|
<svg stroke="currentColor" fill="none" viewBox="0 0 24 24" stroke-width="1.8" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M9 15 3 9m0 0 6-6M3 9h12a6 6 0 0 1 0 12h-3"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div class="arr-cal-header-right">
|
|
<button class="arr-cal-nav" type="button" data-arr-nav="prev" title="Previous month" aria-label="Previous month">
|
|
<svg stroke="currentColor" fill="none" viewBox="0 0 24 24" stroke-width="2" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M14 6 8 12l6 6"></path>
|
|
</svg>
|
|
</button>
|
|
<div class="arr-cal-monthnum" data-arr-monthnum></div>
|
|
<button class="arr-cal-nav" type="button" data-arr-nav="next" title="Next month" aria-label="Next month">
|
|
<svg stroke="currentColor" fill="none" viewBox="0 0 24 24" stroke-width="2" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="m10 6 6 6-6 6"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="arr-calendar-grid" data-arr-grid></div>
|
|
<div class="arr-calendar-popover" data-arr-popover hidden></div>
|
|
|
|
<ul class="arr-release-data" hidden>
|
|
{{- if and $radarrEnabled (eq $radarrData.Response.StatusCode 200) }}
|
|
{{- range $radarrData.JSON.Array "" }}
|
|
{{- $title := .String "title" }}
|
|
{{- $id := .Int "id" }}
|
|
{{- $titleSlug := .String "titleSlug" }}
|
|
{{- $link := printf "%s/movie/%s" $radarrUrl $titleSlug }}
|
|
{{- $coverUrl := printf "%s/api/v3/mediacover/%d/poster-250.jpg?apikey=%s" $radarrUrl $id $radarrKey }}
|
|
|
|
{{- $releaseDate := .String "releaseDate" }}
|
|
{{- $releaseType := "Release" }}
|
|
{{- $selectedDate := $releaseDate }}
|
|
|
|
{{- $inCinemas := "" }}{{ if .Exists "inCinemas" }}{{ $inCinemas = .String "inCinemas" }}{{ end }}
|
|
{{- $digitalRelease := "" }}{{ if .Exists "digitalRelease" }}{{ $digitalRelease = .String "digitalRelease" }}{{ end }}
|
|
{{- $physicalRelease := "" }}{{ if .Exists "physicalRelease" }}{{ $physicalRelease = .String "physicalRelease" }}{{ end }}
|
|
|
|
{{- if and (ne $physicalRelease "") (gt $physicalRelease $nowIso) }}
|
|
{{- $releaseType = "Physical" }}
|
|
{{- $selectedDate = $physicalRelease }}
|
|
{{- end }}
|
|
{{- if and (ne $digitalRelease "") (gt $digitalRelease $nowIso) }}
|
|
{{- if or (eq $selectedDate $releaseDate) (lt $digitalRelease $selectedDate) }}
|
|
{{- $releaseType = "Digital" }}
|
|
{{- $selectedDate = $digitalRelease }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- if and (ne $inCinemas "") (gt $inCinemas $nowIso) }}
|
|
{{- if or (eq $selectedDate $releaseDate) (lt $inCinemas $selectedDate) }}
|
|
{{- $releaseType = "In Cinemas" }}
|
|
{{- $selectedDate = $inCinemas }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
{{- if or (not $excludePhysical) (ne $releaseType "Physical") }}
|
|
{{- $dateOnly := $selectedDate | parseTime "RFC3339" | formatTime "2006-01-02" }}
|
|
<li data-service="radarr" data-date="{{ $dateOnly }}" data-title="{{ $title }}" data-subtitle="{{ $releaseType }}" data-link="{{ $link }}" data-cover="{{ $coverUrl }}"></li>
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- if and $sonarrEnabled (eq $sonarrData.Response.StatusCode 200) }}
|
|
{{- range $sonarrData.JSON.Array "" }}
|
|
{{- $seriesId := .Int "seriesId" }}
|
|
{{- $coverUrl := printf "%s/api/v3/mediacover/%d/poster-250.jpg?apikey=%s" $sonarrUrl $seriesId $sonarrKey }}
|
|
{{- $title := "" }}{{ if .Exists "series.title" }}{{ $title = .String "series.title" }}{{ end }}
|
|
{{- $titleSlug := "" }}{{ if .Exists "series.titleSlug" }}{{ $titleSlug = .String "series.titleSlug" }}{{ end }}
|
|
{{- $link := printf "%s/series/%s" $sonarrUrl $titleSlug }}
|
|
{{- $dateOnly := .String "airDateUtc" | parseTime "RFC3339" | formatTime "2006-01-02" }}
|
|
{{- $seString := printf "S%02dE%02d" (.Int "seasonNumber") (.Int "episodeNumber") }}
|
|
{{- $epTitle := .String "title" }}
|
|
{{- $subtitle := printf "%s - %s" $seString $epTitle }}
|
|
<li data-service="sonarr" data-date="{{ $dateOnly }}" data-title="{{ $title }}" data-subtitle="{{ $subtitle }}" data-link="{{ $link }}" data-cover="{{ $coverUrl }}"></li>
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- if and $lidarrEnabled (eq $lidarrData.Response.StatusCode 200) }}
|
|
{{- range $lidarrData.JSON.Array "" }}
|
|
{{- $albumId := .Int "id" }}
|
|
{{- $title := "" }}{{ if .Exists "title" }}{{ $title = .String "title" }}{{ end }}
|
|
{{- $artistTitle := "" }}{{ if .Exists "artist.artistName" }}{{ $artistTitle = .String "artist.artistName" }}{{ end }}
|
|
{{- $foreignAlbumId := "" }}{{ if .Exists "foreignAlbumId" }}{{ $foreignAlbumId = .String "foreignAlbumId" }}{{ end }}
|
|
{{- $link := printf "%s/album/%s" $lidarrUrl $foreignAlbumId }}
|
|
{{- $coverUrl := printf "%s/api/v1/MediaCover/album/%d/poster-250.jpg?apikey=%s" $lidarrUrl $albumId $lidarrKey }}
|
|
{{- if .Exists "releaseDate" }}
|
|
{{- $dateOnly := .String "releaseDate" | parseTime "RFC3339" | formatTime "2006-01-02" }}
|
|
<li data-service="lidarr" data-date="{{ $dateOnly }}" data-title="{{ $title }}" data-subtitle="{{ $artistTitle }}" data-link="{{ $link }}" data-cover="{{ $coverUrl }}"></li>
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- end }}
|
|
</ul>
|
|
|
|
<ul class="arr-calendar-status" hidden>
|
|
{{- if $sonarrEnabled }}
|
|
<li data-service="sonarr" data-configured="true" data-ok="{{ eq $sonarrData.Response.StatusCode 200 }}"></li>
|
|
{{- else }}
|
|
<li data-service="sonarr" data-configured="false" data-ok="true"></li>
|
|
{{- end }}
|
|
{{- if $radarrEnabled }}
|
|
<li data-service="radarr" data-configured="true" data-ok="{{ eq $radarrData.Response.StatusCode 200 }}"></li>
|
|
{{- else }}
|
|
<li data-service="radarr" data-configured="false" data-ok="true"></li>
|
|
{{- end }}
|
|
{{- if $lidarrEnabled }}
|
|
<li data-service="lidarr" data-configured="true" data-ok="{{ eq $lidarrData.Response.StatusCode 200 }}"></li>
|
|
{{- else }}
|
|
<li data-service="lidarr" data-configured="false" data-ok="true"></li>
|
|
{{- end }}
|
|
</ul>
|
|
</div>
|
|
|
|
{{- /* Soft empty-state message when the user hasn't configured any service.
|
|
We intentionally do NOT render the giant widget-error banner — partial
|
|
availability (e.g. Sonarr-only) is success, not an error. */}}
|
|
{{ if not $anyConfigured }}
|
|
<div class="arr-calendar-empty">
|
|
Configure at least one of <code>SONARR_KEY</code>, <code>RADARR_KEY</code>,
|
|
or <code>LIDARR_KEY</code> in the container environment to show releases.
|
|
</div>
|
|
{{ end }}
|