Initial commit

This commit is contained in:
2026-02-24 16:34:39 +03:00
commit ea8fff629c
38 changed files with 4045 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
# Glance main config
# See: https://github.com/glanceapp/glance/blob/main/docs/configuration.md
server:
assets-path: /app/assets
proxied: true
theme:
custom-css-file: /assets/user.css
pages:
- name: Home
columns:
- size: small
widgets:
- type: clock
- type: calendar
first-day-of-week: monday
- type: releases
cache: 1d
repositories:
- glanceapp/glance
- immich-app/immich
- size: full
widgets:
- type: hacker-news
limit: 15
collapse-after: 5
- type: lobsters
sort-by: hot
limit: 15
collapse-after: 5
- type: reddit
subreddit: unixporn
limit: 15
collapse-after: 5
- size: small
widgets:
- type: bookmarks
title: Services
groups:
- same-tab: true
links:
- title: Jellyfin
url: https://jellyfin.home
icon: mdi:television-play
- title: Seer
url: https://jellyseerr.home
icon: mdi:movie-search
- title: Radarr
url: https://radarr.home
icon: mdi:film
- title: Sonarr
url: https://sonarr.home
icon: mdi:television
- title: Lidarr
url: https://lidarr.home
icon: mdi:music
- title: Prowlarr
url: https://prowlarr.home
icon: mdi:radar
- title: qBittorrent
url: https://qbit.home
icon: mdi:download
- title: Portainer
url: https://portainer.home
icon: mdi:docker
- title: Immich
url: https://photo.home
icon: mdi:image-multiple
- title: Navidrome
url: https://music.home
icon: mdi:music-box-multiple
- title: CopyParty
url: https://copyparty.home
icon: mdi:folder-multiple
- title: Gitea
url: https://git.home
icon: mdi:git
- title: Grafana
url: https://grafana.home
icon: mdi:chart-line
- name: Media
columns:
- size: full
widgets:
- type: group
widgets:
- $include: widgets/arr-radarr-upcoming.yml
- $include: widgets/arr-radarr-recent.yml
- $include: widgets/arr-radarr-missing.yml
- $include: widgets/arr-sonarr-upcoming.yml
- $include: widgets/arr-sonarr-recent.yml
- $include: widgets/arr-sonarr-missing.yml
- $include: widgets/monitor-media.yml
- $include: pages/services.yml
- $include: pages/gaming.yml

View File

@@ -0,0 +1,39 @@
- name: Gaming
columns:
- size: small
widgets:
- type: twitch-top-games
limit: 20
collapse-after: 13
exclude:
- just-chatting
- pools-hot-tubs-and-beaches
- music
- art
- asmr
- size: full
widgets:
- type: group
widgets:
- type: reddit
show-thumbnails: true
subreddit: pcgaming
- type: reddit
subreddit: games
- type: videos
style: grid-cards
collapse-after-rows: 3
channels:
- UCNvzD7Z-g64bPXxGzaQaa4g # gameranx
- UCZ7AeeVbyslLM_8-nVy2B8Q # Skill Up
- UCHDxYLv8iovIbhrfl16CNyg # GameLinked
- UC9PBzalIcEQCsiIkq36PyUA # Digital Foundry
- size: small
widgets:
- type: reddit
subreddit: gamingnews
limit: 7
style: vertical-cards

View File

@@ -0,0 +1,16 @@
# Home: clock, calendar, quick links to all services
name: Home
columns:
- size: small
widgets:
- type: clock
- type: calendar
first-day-of-week: monday
- $include: ../widgets/bookmarks-services.yml
- size: full
widgets:
- type: releases
cache: 1d
repositories:
- glanceapp/glance
- immich-app/immich

View File

@@ -0,0 +1,15 @@
# Media: Jellyfin (next up + latest), *arr wanted, status
name: Media
columns:
- size: full
widgets:
- type: group
widgets:
- $include: ../widgets/arr-radarr-upcoming.yml
- $include: ../widgets/arr-radarr-recent.yml
- $include: ../widgets/arr-radarr-missing.yml
- $include: ../widgets/arr-sonarr-upcoming.yml
- $include: ../widgets/arr-sonarr-recent.yml
- $include: ../widgets/arr-sonarr-missing.yml
- $include: ../widgets/jellyfin.yml
- $include: ../widgets/monitor-media.yml

View File

@@ -0,0 +1,83 @@
# Services: status monitor, Docker containers, Portainer
- name: Services
columns:
- size: full
widgets:
- type: server-stats
title: Server
servers:
- type: local
name: Services
mountpoints:
"/":
name: Root
"/srv/media":
name: Media
"/backup":
name: Backup
- type: monitor
title: Service status
sites:
- title: Jellyfin
url: https://jellyfin.home
icon: mdi:television-play
- title: Seer
url: https://jellyseerr.home
icon: mdi:movie-search
- title: Radarr
url: https://radarr.home
icon: mdi:film
- title: Sonarr
url: https://sonarr.home
icon: mdi:television
- title: Lidarr
url: https://lidarr.home
icon: mdi:music
- title: Prowlarr
url: https://prowlarr.home
icon: mdi:radar
- title: qBittorrent
url: https://qbit.home
icon: mdi:download
- title: Portainer
url: https://portainer.home
icon: mdi:docker
- title: Immich
url: https://photo.home
icon: mdi:image-multiple
- title: Navidrome
url: https://music.home
icon: mdi:music-box-multiple
- title: CopyParty
url: https://copyparty.home
icon: mdi:folder-multiple
- title: Gitea
url: https://git.home
icon: mdi:git
- title: Grafana
url: https://grafana.home
icon: mdi:chart-line
- type: docker-containers
title: Docker
show-stats: true
- type: custom-api
title: Portainer
title-url: https://portainer.home
cache: 1h
url: ${PORTAINER_URL}/api/endpoints/${PORTAINER_ENDPOINT_ID}
headers:
X-API-Key: ${PORTAINER_API_KEY}
template: |
<style>.list-horizontal-text.no-bullets > *:not(:last-child)::after { content: none !important; } .list-horizontal-text.no-bullets > *:not(:last-child) { margin-right: 1em; }</style>
<div class="list list-horizontal-text no-bullets">
<span><strong>{{ .JSON.String "Name" }}</strong></span>
<span>Containers: {{ .JSON.Int "Snapshots.0.ContainerCount" }}</span>
<span>Volumes: {{ .JSON.Int "Snapshots.0.VolumeCount" }}</span>
<span>Networks: {{ .JSON.Int "Snapshots.0.DockerSnapshotRaw.Networks.#" }}</span>
</div>
<ul class="list list-gap-4 margin-top-10">
<li>Running <strong>{{ .JSON.Int "Snapshots.0.RunningContainerCount" }}</strong></li>
<li>Healthy <strong>{{ .JSON.Int "Snapshots.0.HealthyContainerCount" }}</strong></li>
<li>Stopped <strong>{{ .JSON.Int "Snapshots.0.StoppedContainerCount" }}</strong></li>
<li>Unhealthy <strong>{{ .JSON.Int "Snapshots.0.DockerSnapshotRaw.Containers.#(Health=\"unhealthy\").#" }}</strong></li>
</ul>

View File

@@ -0,0 +1,450 @@
# *arr community widget: Radarr wanted movies
- type: custom-api
title: Wanted Movies
title-url: ${RADARR_URL}
cache: 30m
options:
service: radarr
type: missing
size: medium
collapse-after: 5
show-grabbed: false
interval: 20
api-base-url: ${RADARR_API_URL}
key: ${RADARR_KEY}
url: ${RADARR_URL}
template: |
{{ $collapseAfter := .Options.IntOr "collapse-after" 5 }}
{{ $showGrabbed := .Options.BoolOr "show-grabbed" false }}
{{ $sortTime := .Options.StringOr "sort-time" "desc" }}
{{ $coverProxy := .Options.StringOr "cover-proxy" "" }}
{{ $size := .Options.StringOr "size" "medium" }}
{{ $service := .Options.StringOr "service" "" }}
{{ $type := .Options.StringOr "type" "" }}
{{ $intervalH := .Options.IntOr "interval" 0 | mul 24 }}
{{ $now := now | formatTime "2006-01-02T15:04:05" }}
{{ $posInterval := (offsetNow (printf "+%dh" $intervalH)) | formatTime "2006-01-02T15:04:05" }}
{{ $negInterval := (offsetNow (printf "-%dh" $intervalH)) | formatTime "2006-01-02T15:04:05" }}
{{ $apiBaseUrl := .Options.StringOr "api-base-url" "" }}
{{ $key := .Options.StringOr "key" "" }}
{{ $url := .Options.StringOr "url" $apiBaseUrl }}
{{ if or (and (ne $service "sonarr") (ne $service "radarr") (ne $service "lidarr"))
(and (ne $type "upcoming") (ne $type "recent") (ne $type "missing"))
(eq $apiBaseUrl "") (eq $key "") (eq $url "") }}
<div class="widget-error-header">
<div class="color-negative size-h3">ERROR</div>
<svg class="widget-error-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"></path>
</svg>
</div>
<p class="break-all">
Some options are not set or malformed
<table style="border-spacing: 1rem;">
<tr><td>service</td><td>{{ $service }}</td><td>must be sonarr, radarr, or lidarr</td></tr>
<tr><td>type</td><td>{{ $type }}</td><td>must be upcoming, recent, or missing</td></tr>
<tr><td>api-base-url</td><td>{{ $apiBaseUrl }}</td><td>should include http(s):// and port if needed</td></tr>
<tr><td>key</td><td>{{ $key }}</td><td></td></tr>
</table>
</p>
{{ else }}
{{ $requestUrl := "" }}
{{ if eq $service "sonarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v3/history/since?includeSeries=true&includeEpisode=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v3/wanted/missing?page=1&pageSize=50&includeSeries=true" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v3/calendar?includeSeries=true&start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ else if eq $service "radarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v3/history/since?includeMovie=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v3/wanted/missing?page=1&pageSize=50" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v3/calendar?start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ else if eq $service "lidarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v1/history/since?includeArtist=true&includeAlbum=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v1/wanted/missing?page=1&pageSize=50&includeArtist=true" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v1/calendar?includeArtist=true&start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ end }}
{{ $data := newRequest $requestUrl
| withHeader "Accept" "application/json"
| withHeader "X-Api-Key" $key
| getResponse }}
<ul class="list list-gap-14 collapsible-container single-line-titles" data-collapse-after="{{ $collapseAfter }}">
{{ $array := "" }}
{{ $sortByField := "" }}
{{ $itemDisplayed := false }}
{{ $itemDate := "" }}
{{ $isAvailable := false }}
{{ if eq $type "missing" }}
{{ $array = "records" }}
{{ if eq $service "sonarr" }}
{{ $sortByField = "airDateUtc" }}
{{ else }}
{{ $sortByField = "releaseDate" }}
{{ end }}
{{ else if eq $type "recent" }}
{{ $sortByField = "date" }}
{{ else }}
{{ if eq $service "sonarr" }}
{{ $sortByField = "airDateUtc" }}
{{ else }}
{{ $sortByField = "releaseDate" }}
{{ end }}
{{ end }}
{{ range $data.JSON.Array $array | sortByTime $sortByField "rfc3339" $sortTime }}
{{ if eq $service "sonarr" }}
{{ $itemDate = .String "airDateUtc" | parseTime "RFC3339" }}
{{ $itemDate = $itemDate.In now.Location | formatTime "2006-01-02T15:04:05" }}
{{ $isAvailable = true }}
{{ else if eq $service "radarr"}}
{{ $isAvailable = .Bool "isAvailable" }}
{{ $itemDate = .String "releaseDate" }}
{{ else if eq $service "lidarr"}}
{{ $itemDate = .String "releaseDate" }}
{{ $isAvailable = true }}
{{ end }}
{{ if or (eq $type "upcoming") (eq $type "recent") (and (or (and (gt $itemDate $negInterval) ((lt $itemDate $now ))) (eq $intervalH 0)) $isAvailable) }}
{{ $itemDisplayed = true }}
{{ $title := "" }}
{{ $subtitle := "" }}
{{ $coverUrl := "" }}
{{ $status := "" }}
{{ $coverBase := "" }}
{{ $height := "" }}
{{ $width := "" }}
{{ $popoverTitle := "" }}
{{ $popoverSubtitle := "" }}
{{ $popoverSummary := "" }}
{{ $summary := "" }}
{{ $link := "" }}
{{ $grabbed := false }}
{{ $date := now }}
{{ $datetype := "" }}
{{ $seString := "" }}
{{ $genres := "" }}
{{ $buttonJustify := "left" }}
{{ if eq $service "sonarr" }}
{{ $series := "" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v3/mediacover" $apiBaseUrl }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "seriesId") $key }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "seriesId") }}
{{ end }}
{{ $title = .String "series.title" }}
{{ $link = printf "%s/series/%s#" $url (.String "series.titleSlug") }}
{{ $series = .Get "series" }}
{{ $genres = $series.Get "genres" }}
{{ if eq $type "recent" }}
{{ $date = .String "date" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "episode.title" }}
{{ $summary = .String "episode.overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "episode.seasonNumber") (.Int "episode.episodeNumber") }}
{{ $datetype = "Downloaded" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "episode.title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "episode.overview" }}
{{ if .Bool "episode.hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ else if eq $type "missing" }}
{{ $date = .String "airDateUtc" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "title" }}
{{ $summary = .String "overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "seasonNumber") (.Int "episodeNumber") }}
{{ $datetype = "Aired" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "overview" }}
{{ if .Bool "episode.hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ else if eq $type "upcoming" }}
{{ $date = .String "airDateUtc" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "title" }}
{{ $summary = .String "overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "seasonNumber") (.Int "episodeNumber") }}
{{ $datetype = "Airs" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "overview" }}
{{ if .Bool "hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ end }}
{{ else if eq $service "radarr" }}
{{ $movie := "" }}
{{ $status = .String "status" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v3/mediacover" $apiBaseUrl }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ end }}
{{ if eq $status "announced"}}
{{ if ne (.String "inCinemas") "" }}
{{ $date = (.String "inCinemas" | parseTime "RFC3339") }}
{{ $datetype = "In Cinemas" }}
{{ else }}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Releases" }}
{{ end }}
{{ else if eq $status "inCinemas"}}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Releases" }}
{{ else if eq $status "released"}}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Released" }}
{{ end }}
{{ if eq $type "recent" }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "movie.id") $key }}
{{ else }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "movie.id") }}
{{ end }}
{{ $datetype = "Downloaded" }}
{{ $date = (.String "date" | parseTime "RFC3339") }}
{{ $title = .String "movie.title" }}
{{ $subtitle = "" }}
{{ $summary = .String "movie.overview" }}
{{ $popoverTitle = .String "movie.title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "movie.overview" }}
{{ $link = printf "%s/movie/%s#" $url (.String "movie.titleSlug") }}
{{ $movie = .Get "movie" }}
{{ $genres = $movie.Get "genres" }}
{{ if and $showGrabbed (gt (.Int "movie.movieFileId") 0) }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "id") $key }}
{{ else }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "id") }}
{{ end }}
{{ $title = .String "title" }}
{{ $subtitle = "" }}
{{ $summary = .String "overview" }}
{{ $link = printf "%s/movie/%s#" $url (.String "titleSlug") }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "overview" }}
{{ $genres = .Get "genres" }}
{{ if eq $type "missing" }}
{{ if and $showGrabbed (.Bool "movie.hasFile") }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ if and $showGrabbed (.Bool "hasFile") }}
{{ $grabbed = true }}
{{ end }}
{{ end }}
{{ end }}
{{ else if eq $service "lidarr" }}
{{ $artist := "" }}
{{ $album := "" }}
{{ $albumId := "" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v1/mediacover" $apiBaseUrl }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ end }}
{{ if eq $type "recent" }}
{{ $album = .Get "album" }}
{{ $artist = $album.Get "artist" }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg?apikey=%s" $coverBase (.String "albumId") $key }}
{{ else }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg" $coverBase (.String "albumId") }}
{{ end }}
{{ $grabbed = true }}
{{ $title = $album.String "title" }}
{{ $date = (.String "date" | parseTime "RFC3339") }}
{{ $datetype = "Downloaded" }}
{{ $subtitle = $artist.String "artistName" }}
{{ $genres = $artist.Get "genres" }}
{{ $link = printf "%s/artist/%s#" $url ($artist.String "foreignArtistId") }}
{{ $summary = $album.String "overview" }}
{{ $popoverTitle = $album.String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = $artist.String "overview" }}
{{ else }}
{{ $artist = .Get "artist" }}
{{ $album = .Get "album" }}
{{ if eq $type "missing" }}
{{ $datetype = "Released" }}
{{ else }}
{{ $datetype = "Releases" }}
{{ end }}
{{ range .Array "releases" }}
{{ $albumId = .String "albumId" }}
{{ break }}
{{ end }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg?apikey=%s" $coverBase $albumId $key }}
{{ else }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg" $coverBase $albumId }}
{{ end }}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $title = .String "title" }}
{{ $subtitle = $artist.String "artistName" }}
{{ $summary = $album.String "overview" }}
{{ $genres = $artist.Get "genres" }}
{{ $link = printf "%s/artist/%s#" $url ($artist.String "foreignArtistId") }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "artist.overview" }}
{{ end }}
{{ end }}
{{ if eq $size "small" }}
{{ $buttonJustify = "right" }}
{{ $height = "9rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "9rem" }}
{{ else }}
{{ $width = "6rem" }}
{{ end }}
{{ else if eq $size "medium" }}
{{ $height = "12rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "12rem" }}
{{ else }}
{{ $width = "8rem" }}
{{ end }}
{{ else if eq $size "large" }}
{{ $height = "15rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "15rem" }}
{{ else }}
{{ $width = "10rem" }}
{{ end }}
{{ else if eq $size "huge" }}
{{ $height = "18rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "18rem" }}
{{ else }}
{{ $width = "12rem" }}
{{ end }}
{{ end }}
<li style="position: relative;">
<div class="flex gap-10 items-start thumbnail-container thumbnail-parent">
<div>
<div data-popover-type="html" data-popover-position="above" data-popover-show-delay="500" style="width: {{ $width }}; height: {{ $height }}; align-content: center;">
<div data-popover-html>
<div style="margin: 5px;">
<strong class="size-h4 color-primary" title="{{ $popoverTitle }}">{{ $popoverTitle }}</strong>
<div class="size-h4 text-truncate text-very-compact color-subdue" title="{{ $popoverSubtitle }}">{{ $popoverSubtitle }}</div>
<p class="margin-top-20" style="overflow-y: auto; text-align: justify; max-height: 20rem;">
{{ if ne $popoverSummary "" }}
{{ $popoverSummary }}
{{ else }}
TBA
{{ end }}
</p>
{{ if gt (len ($genres.Array "")) 0 }}
<ul class="attachments margin-top-20">
{{ range $genres.Array "" }}
<li>{{ .String "" }}</li>
{{ end }}
</ul>
{{ end }}
</div>
</div>
<img class="thumbnail" src="{{ $coverUrl }}" alt="Cover for {{ .String "title" }}" loading="lazy" style="width: 100%; height: 100%; box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); object-fit: cover; border-radius: 0.5rem;">
</div>
</div>
<div class="shrink min-width-0" style="height: 9rem; position: relative; padding-top: 5px; padding-right: 5px;">
<strong class="size-h4 block text-truncate color-primary" title="{{ $title }}">{{ $title }}</strong>
<div class="text-truncate text-very-compact" title="{{ $subtitle }}">{{ $subtitle }}</div>
<div class="text-very-compact text-truncate">
{{ if eq $service "sonarr" }}
<div>{{ $seString }} - {{ $datetype }} {{ $date.Format "1/2 03:04PM" }}</div>
{{ else }}
<span>{{ $datetype }} {{ $date.Format "1/2" }}</span>
{{ end }}
</div>
{{ if $showGrabbed }}
{{ if eq $buttonJustify "right" }}
</div>
<a href="{{ $link }}" class="bookmarks-link size-h4 color-primary" target="_blank" rel="noreferrer"
style="position: absolute; bottom: 1rem; right: 1rem;
{{ if $grabbed }} color: var(--color-positive); border: 1px solid var(--color-positive); {{ else }} color: var(--color-negative); border: 1px solid var(--color-negative); {{ end }}
font-weight: bold; padding: 2px 5px; border-radius: 3px; display: inline-block; margin-top: 5px; text-decoration: none;">
{{ if $grabbed }}Grabbed{{ else }}Missing{{ end }}
</a>
{{ else }}
<a href="{{ $link }}" class="bookmarks-link size-h4 color-primary" target="_blank" rel="noreferrer"
style="{{ if $grabbed }} color: var(--color-positive); border: 1px solid var(--color-positive); {{ else }} color: var(--color-negative); border: 1px solid var(--color-negative); {{ end }}
font-weight: bold; padding: 2px 5px; border-radius: 3px; display: inline-block; margin-top: 5px; text-decoration: none;">
{{ if $grabbed }}Grabbed{{ else }}Missing{{ end }}
</a>
</div>
{{ end }}
{{ else }}
<div class="{{ if eq $size "small" }}text-truncate{{ if eq $service "radarr" }} margin-top-5{{ end }}{{ else }}text-truncate-2-lines margin-top-5{{ end }}">
{{ $summary }}
</div>
{{ end }}
</div>
</li>
{{ end }}
{{ end }}
{{ if not $itemDisplayed }}
<li>No items found.</li>
{{ end }}
</ul>
{{ end }}

View File

@@ -0,0 +1,449 @@
# *arr community widget: Radarr recent movies
- type: custom-api
title: Recent Movies
title-url: ${RADARR_URL}
cache: 30m
options:
service: radarr
type: recent
size: medium
collapse-after: 5
show-grabbed: false
interval: 20
api-base-url: ${RADARR_API_URL}
key: ${RADARR_KEY}
url: ${RADARR_URL}
template: |
{{ $collapseAfter := .Options.IntOr "collapse-after" 5 }}
{{ $showGrabbed := .Options.BoolOr "show-grabbed" false }}
{{ $sortTime := .Options.StringOr "sort-time" "desc" }}
{{ $coverProxy := .Options.StringOr "cover-proxy" "" }}
{{ $size := .Options.StringOr "size" "medium" }}
{{ $service := .Options.StringOr "service" "" }}
{{ $type := .Options.StringOr "type" "" }}
{{ $intervalH := .Options.IntOr "interval" 0 | mul 24 }}
{{ $now := now | formatTime "2006-01-02T15:04:05" }}
{{ $posInterval := (offsetNow (printf "+%dh" $intervalH)) | formatTime "2006-01-02T15:04:05" }}
{{ $negInterval := (offsetNow (printf "-%dh" $intervalH)) | formatTime "2006-01-02T15:04:05" }}
{{ $apiBaseUrl := .Options.StringOr "api-base-url" "" }}
{{ $key := .Options.StringOr "key" "" }}
{{ $url := .Options.StringOr "url" $apiBaseUrl }}
{{ if or (and (ne $service "sonarr") (ne $service "radarr") (ne $service "lidarr"))
(and (ne $type "upcoming") (ne $type "recent") (ne $type "missing"))
(eq $apiBaseUrl "") (eq $key "") (eq $url "") }}
<div class="widget-error-header">
<div class="color-negative size-h3">ERROR</div>
<svg class="widget-error-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"></path>
</svg>
</div>
<p class="break-all">
Some options are not set or malformed
<table style="border-spacing: 1rem;">
<tr><td>service</td><td>{{ $service }}</td><td>must be sonarr, radarr, or lidarr</td></tr>
<tr><td>type</td><td>{{ $type }}</td><td>must be upcoming, recent, or missing</td></tr>
<tr><td>api-base-url</td><td>{{ $apiBaseUrl }}</td><td>should include http(s):// and port if needed</td></tr>
<tr><td>key</td><td>{{ $key }}</td><td></td></tr>
</table>
</p>
{{ else }}
{{ $requestUrl := "" }}
{{ if eq $service "sonarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v3/history/since?includeSeries=true&includeEpisode=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v3/wanted/missing?page=1&pageSize=50&includeSeries=true" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v3/calendar?includeSeries=true&start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ else if eq $service "radarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v3/history/since?includeMovie=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v3/wanted/missing?page=1&pageSize=50" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v3/calendar?start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ else if eq $service "lidarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v1/history/since?includeArtist=true&includeAlbum=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v1/wanted/missing?page=1&pageSize=50&includeArtist=true" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v1/calendar?includeArtist=true&start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ end }}
{{ $data := newRequest $requestUrl
| withHeader "Accept" "application/json"
| withHeader "X-Api-Key" $key
| getResponse }}
<ul class="list list-gap-14 collapsible-container single-line-titles" data-collapse-after="{{ $collapseAfter }}">
{{ $array := "" }}
{{ $sortByField := "" }}
{{ $itemDisplayed := false }}
{{ $itemDate := "" }}
{{ $isAvailable := false }}
{{ if eq $type "missing" }}
{{ $array = "records" }}
{{ if eq $service "sonarr" }}
{{ $sortByField = "airDateUtc" }}
{{ else }}
{{ $sortByField = "releaseDate" }}
{{ end }}
{{ else if eq $type "recent" }}
{{ $sortByField = "date" }}
{{ else }}
{{ if eq $service "sonarr" }}
{{ $sortByField = "airDateUtc" }}
{{ else }}
{{ $sortByField = "releaseDate" }}
{{ end }}
{{ end }}
{{ range $data.JSON.Array $array | sortByTime $sortByField "rfc3339" $sortTime }}
{{ if eq $service "sonarr" }}
{{ $itemDate = .String "airDateUtc" | parseTime "RFC3339" }}
{{ $itemDate = $itemDate.In now.Location | formatTime "2006-01-02T15:04:05" }}
{{ $isAvailable = true }}
{{ else if eq $service "radarr"}}
{{ $isAvailable = .Bool "isAvailable" }}
{{ $itemDate = .String "releaseDate" }}
{{ else if eq $service "lidarr"}}
{{ $itemDate = .String "releaseDate" }}
{{ $isAvailable = true }}
{{ end }}
{{ if or (eq $type "upcoming") (eq $type "recent") (and (or (and (gt $itemDate $negInterval) ((lt $itemDate $now ))) (eq $intervalH 0)) $isAvailable) }}
{{ $itemDisplayed = true }}
{{ $title := "" }}
{{ $subtitle := "" }}
{{ $coverUrl := "" }}
{{ $status := "" }}
{{ $coverBase := "" }}
{{ $height := "" }}
{{ $width := "" }}
{{ $popoverTitle := "" }}
{{ $popoverSubtitle := "" }}
{{ $popoverSummary := "" }}
{{ $summary := "" }}
{{ $link := "" }}
{{ $grabbed := false }}
{{ $date := now }}
{{ $datetype := "" }}
{{ $seString := "" }}
{{ $genres := "" }}
{{ $buttonJustify := "left" }}
{{ if eq $service "sonarr" }}
{{ $series := "" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v3/mediacover" $apiBaseUrl }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "seriesId") $key }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "seriesId") }}
{{ end }}
{{ $title = .String "series.title" }}
{{ $link = printf "%s/series/%s#" $url (.String "series.titleSlug") }}
{{ $series = .Get "series" }}
{{ $genres = $series.Get "genres" }}
{{ if eq $type "recent" }}
{{ $date = .String "date" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "episode.title" }}
{{ $summary = .String "episode.overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "episode.seasonNumber") (.Int "episode.episodeNumber") }}
{{ $datetype = "Downloaded" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "episode.title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "episode.overview" }}
{{ if .Bool "episode.hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ else if eq $type "missing" }}
{{ $date = .String "airDateUtc" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "title" }}
{{ $summary = .String "overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "seasonNumber") (.Int "episodeNumber") }}
{{ $datetype = "Aired" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "overview" }}
{{ if .Bool "episode.hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ else if eq $type "upcoming" }}
{{ $date = .String "airDateUtc" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "title" }}
{{ $summary = .String "overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "seasonNumber") (.Int "episodeNumber") }}
{{ $datetype = "Airs" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "overview" }}
{{ if .Bool "hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ end }}
{{ else if eq $service "radarr" }}
{{ $movie := "" }}
{{ $status = .String "status" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v3/mediacover" $apiBaseUrl }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ end }}
{{ if eq $status "announced"}}
{{ if ne (.String "inCinemas") "" }}
{{ $date = (.String "inCinemas" | parseTime "RFC3339") }}
{{ $datetype = "In Cinemas" }}
{{ else }}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Releases" }}
{{ end }}
{{ else if eq $status "inCinemas"}}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Releases" }}
{{ else if eq $status "released"}}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Released" }}
{{ end }}
{{ if eq $type "recent" }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "movie.id") $key }}
{{ else }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "movie.id") }}
{{ end }}
{{ $datetype = "Downloaded" }}
{{ $date = (.String "date" | parseTime "RFC3339") }}
{{ $title = .String "movie.title" }}
{{ $subtitle = "" }}
{{ $summary = .String "movie.overview" }}
{{ $popoverTitle = .String "movie.title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "movie.overview" }}
{{ $link = printf "%s/movie/%s#" $url (.String "movie.titleSlug") }}
{{ $movie = .Get "movie" }}
{{ $genres = $movie.Get "genres" }}
{{ if and $showGrabbed (gt (.Int "movie.movieFileId") 0) }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "id") $key }}
{{ else }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "id") }}
{{ end }}
{{ $title = .String "title" }}
{{ $subtitle = "" }}
{{ $summary = .String "overview" }}
{{ $link = printf "%s/movie/%s#" $url (.String "titleSlug") }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "overview" }}
{{ $genres = .Get "genres" }}
{{ if eq $type "missing" }}
{{ if and $showGrabbed (.Bool "movie.hasFile") }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ if and $showGrabbed (.Bool "hasFile") }}
{{ $grabbed = true }}
{{ end }}
{{ end }}
{{ end }}
{{ else if eq $service "lidarr" }}
{{ $artist := "" }}
{{ $album := "" }}
{{ $albumId := "" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v1/mediacover" $apiBaseUrl }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ end }}
{{ if eq $type "recent" }}
{{ $album = .Get "album" }}
{{ $artist = $album.Get "artist" }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg?apikey=%s" $coverBase (.String "albumId") $key }}
{{ else }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg" $coverBase (.String "albumId") }}
{{ end }}
{{ $grabbed = true }}
{{ $title = $album.String "title" }}
{{ $date = (.String "date" | parseTime "RFC3339") }}
{{ $datetype = "Downloaded" }}
{{ $subtitle = $artist.String "artistName" }}
{{ $genres = $artist.Get "genres" }}
{{ $link = printf "%s/artist/%s#" $url ($artist.String "foreignArtistId") }}
{{ $summary = $album.String "overview" }}
{{ $popoverTitle = $album.String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = $artist.String "overview" }}
{{ else }}
{{ $artist = .Get "artist" }}
{{ $album = .Get "album" }}
{{ if eq $type "missing" }}
{{ $datetype = "Released" }}
{{ else }}
{{ $datetype = "Releases" }}
{{ end }}
{{ range .Array "releases" }}
{{ $albumId = .String "albumId" }}
{{ break }}
{{ end }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg?apikey=%s" $coverBase $albumId $key }}
{{ else }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg" $coverBase $albumId }}
{{ end }}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $title = .String "title" }}
{{ $subtitle = $artist.String "artistName" }}
{{ $summary = $album.String "overview" }}
{{ $genres = $artist.Get "genres" }}
{{ $link = printf "%s/artist/%s#" $url ($artist.String "foreignArtistId") }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "artist.overview" }}
{{ end }}
{{ end }}
{{ if eq $size "small" }}
{{ $buttonJustify = "right" }}
{{ $height = "9rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "9rem" }}
{{ else }}
{{ $width = "6rem" }}
{{ end }}
{{ else if eq $size "medium" }}
{{ $height = "12rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "12rem" }}
{{ else }}
{{ $width = "8rem" }}
{{ end }}
{{ else if eq $size "large" }}
{{ $height = "15rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "15rem" }}
{{ else }}
{{ $width = "10rem" }}
{{ end }}
{{ else if eq $size "huge" }}
{{ $height = "18rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "18rem" }}
{{ else }}
{{ $width = "12rem" }}
{{ end }}
{{ end }}
<li style="position: relative;">
<div class="flex gap-10 items-start thumbnail-container thumbnail-parent">
<div>
<div data-popover-type="html" data-popover-position="above" data-popover-show-delay="500" style="width: {{ $width }}; height: {{ $height }}; align-content: center;">
<div data-popover-html>
<div style="margin: 5px;">
<strong class="size-h4 color-primary" title="{{ $popoverTitle }}">{{ $popoverTitle }}</strong>
<div class="size-h4 text-truncate text-very-compact color-subdue" title="{{ $popoverSubtitle }}">{{ $popoverSubtitle }}</div>
<p class="margin-top-20" style="overflow-y: auto; text-align: justify; max-height: 20rem;">
{{ if ne $popoverSummary "" }}
{{ $popoverSummary }}
{{ else }}
TBA
{{ end }}
</p>
{{ if gt (len ($genres.Array "")) 0 }}
<ul class="attachments margin-top-20">
{{ range $genres.Array "" }}
<li>{{ .String "" }}</li>
{{ end }}
</ul>
{{ end }}
</div>
</div>
<img class="thumbnail" src="{{ $coverUrl }}" alt="Cover for {{ .String "title" }}" loading="lazy" style="width: 100%; height: 100%; box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); object-fit: cover; border-radius: 0.5rem;">
</div>
</div>
<div class="shrink min-width-0" style="height: 9rem; position: relative; padding-top: 5px; padding-right: 5px;">
<strong class="size-h4 block text-truncate color-primary" title="{{ $title }}">{{ $title }}</strong>
<div class="text-truncate text-very-compact" title="{{ $subtitle }}">{{ $subtitle }}</div>
<div class="text-very-compact text-truncate">
{{ if eq $service "sonarr" }}
<div>{{ $seString }} - {{ $datetype }} {{ $date.Format "1/2 03:04PM" }}</div>
{{ else }}
<span>{{ $datetype }} {{ $date.Format "1/2" }}</span>
{{ end }}
</div>
{{ if $showGrabbed }}
{{ if eq $buttonJustify "right" }}
</div>
<a href="{{ $link }}" class="bookmarks-link size-h4 color-primary" target="_blank" rel="noreferrer"
style="position: absolute; bottom: 1rem; right: 1rem;
{{ if $grabbed }} color: var(--color-positive); border: 1px solid var(--color-positive); {{ else }} color: var(--color-negative); border: 1px solid var(--color-negative); {{ end }}
font-weight: bold; padding: 2px 5px; border-radius: 3px; display: inline-block; margin-top: 5px; text-decoration: none;">
{{ if $grabbed }}Grabbed{{ else }}Missing{{ end }}
</a>
{{ else }}
<a href="{{ $link }}" class="bookmarks-link size-h4 color-primary" target="_blank" rel="noreferrer"
style="{{ if $grabbed }} color: var(--color-positive); border: 1px solid var(--color-positive); {{ else }} color: var(--color-negative); border: 1px solid var(--color-negative); {{ end }}
font-weight: bold; padding: 2px 5px; border-radius: 3px; display: inline-block; margin-top: 5px; text-decoration: none;">
{{ if $grabbed }}Grabbed{{ else }}Missing{{ end }}
</a>
</div>
{{ end }}
{{ else }}
<div class="{{ if eq $size "small" }}text-truncate{{ if eq $service "radarr" }} margin-top-5{{ end }}{{ else }}text-truncate-2-lines margin-top-5{{ end }}">
{{ $summary }}
</div>
{{ end }}
</div>
</li>
{{ end }}
{{ end }}
{{ if not $itemDisplayed }}
<li>No items found.</li>
{{ end }}
</ul>
{{ end }}

View File

@@ -0,0 +1,449 @@
# *arr community widget: Radarr upcoming movies
- type: custom-api
title: Upcoming Movies
title-url: ${RADARR_URL}
cache: 30m
options:
service: radarr
type: upcoming
size: medium
collapse-after: 5
show-grabbed: false
interval: 20
api-base-url: ${RADARR_API_URL}
key: ${RADARR_KEY}
url: ${RADARR_URL}
template: |
{{ $collapseAfter := .Options.IntOr "collapse-after" 5 }}
{{ $showGrabbed := .Options.BoolOr "show-grabbed" false }}
{{ $sortTime := .Options.StringOr "sort-time" "desc" }}
{{ $coverProxy := .Options.StringOr "cover-proxy" "" }}
{{ $size := .Options.StringOr "size" "medium" }}
{{ $service := .Options.StringOr "service" "" }}
{{ $type := .Options.StringOr "type" "" }}
{{ $intervalH := .Options.IntOr "interval" 0 | mul 24 }}
{{ $now := now | formatTime "2006-01-02T15:04:05" }}
{{ $posInterval := (offsetNow (printf "+%dh" $intervalH)) | formatTime "2006-01-02T15:04:05" }}
{{ $negInterval := (offsetNow (printf "-%dh" $intervalH)) | formatTime "2006-01-02T15:04:05" }}
{{ $apiBaseUrl := .Options.StringOr "api-base-url" "" }}
{{ $key := .Options.StringOr "key" "" }}
{{ $url := .Options.StringOr "url" $apiBaseUrl }}
{{ if or (and (ne $service "sonarr") (ne $service "radarr") (ne $service "lidarr"))
(and (ne $type "upcoming") (ne $type "recent") (ne $type "missing"))
(eq $apiBaseUrl "") (eq $key "") (eq $url "") }}
<div class="widget-error-header">
<div class="color-negative size-h3">ERROR</div>
<svg class="widget-error-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"></path>
</svg>
</div>
<p class="break-all">
Some options are not set or malformed
<table style="border-spacing: 1rem;">
<tr><td>service</td><td>{{ $service }}</td><td>must be sonarr, radarr, or lidarr</td></tr>
<tr><td>type</td><td>{{ $type }}</td><td>must be upcoming, recent, or missing</td></tr>
<tr><td>api-base-url</td><td>{{ $apiBaseUrl }}</td><td>should include http(s):// and port if needed</td></tr>
<tr><td>key</td><td>{{ $key }}</td><td></td></tr>
</table>
</p>
{{ else }}
{{ $requestUrl := "" }}
{{ if eq $service "sonarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v3/history/since?includeSeries=true&includeEpisode=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v3/wanted/missing?page=1&pageSize=50&includeSeries=true" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v3/calendar?includeSeries=true&start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ else if eq $service "radarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v3/history/since?includeMovie=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v3/wanted/missing?page=1&pageSize=50" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v3/calendar?start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ else if eq $service "lidarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v1/history/since?includeArtist=true&includeAlbum=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v1/wanted/missing?page=1&pageSize=50&includeArtist=true" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v1/calendar?includeArtist=true&start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ end }}
{{ $data := newRequest $requestUrl
| withHeader "Accept" "application/json"
| withHeader "X-Api-Key" $key
| getResponse }}
<ul class="list list-gap-14 collapsible-container single-line-titles" data-collapse-after="{{ $collapseAfter }}">
{{ $array := "" }}
{{ $sortByField := "" }}
{{ $itemDisplayed := false }}
{{ $itemDate := "" }}
{{ $isAvailable := false }}
{{ if eq $type "missing" }}
{{ $array = "records" }}
{{ if eq $service "sonarr" }}
{{ $sortByField = "airDateUtc" }}
{{ else }}
{{ $sortByField = "releaseDate" }}
{{ end }}
{{ else if eq $type "recent" }}
{{ $sortByField = "date" }}
{{ else }}
{{ if eq $service "sonarr" }}
{{ $sortByField = "airDateUtc" }}
{{ else }}
{{ $sortByField = "releaseDate" }}
{{ end }}
{{ end }}
{{ range $data.JSON.Array $array | sortByTime $sortByField "rfc3339" $sortTime }}
{{ if eq $service "sonarr" }}
{{ $itemDate = .String "airDateUtc" | parseTime "RFC3339" }}
{{ $itemDate = $itemDate.In now.Location | formatTime "2006-01-02T15:04:05" }}
{{ $isAvailable = true }}
{{ else if eq $service "radarr"}}
{{ $isAvailable = .Bool "isAvailable" }}
{{ $itemDate = .String "releaseDate" }}
{{ else if eq $service "lidarr"}}
{{ $itemDate = .String "releaseDate" }}
{{ $isAvailable = true }}
{{ end }}
{{ if or (eq $type "upcoming") (eq $type "recent") (and (or (and (gt $itemDate $negInterval) ((lt $itemDate $now ))) (eq $intervalH 0)) $isAvailable) }}
{{ $itemDisplayed = true }}
{{ $title := "" }}
{{ $subtitle := "" }}
{{ $coverUrl := "" }}
{{ $status := "" }}
{{ $coverBase := "" }}
{{ $height := "" }}
{{ $width := "" }}
{{ $popoverTitle := "" }}
{{ $popoverSubtitle := "" }}
{{ $popoverSummary := "" }}
{{ $summary := "" }}
{{ $link := "" }}
{{ $grabbed := false }}
{{ $date := now }}
{{ $datetype := "" }}
{{ $seString := "" }}
{{ $genres := "" }}
{{ $buttonJustify := "left" }}
{{ if eq $service "sonarr" }}
{{ $series := "" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v3/mediacover" $apiBaseUrl }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "seriesId") $key }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "seriesId") }}
{{ end }}
{{ $title = .String "series.title" }}
{{ $link = printf "%s/series/%s#" $url (.String "series.titleSlug") }}
{{ $series = .Get "series" }}
{{ $genres = $series.Get "genres" }}
{{ if eq $type "recent" }}
{{ $date = .String "date" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "episode.title" }}
{{ $summary = .String "episode.overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "episode.seasonNumber") (.Int "episode.episodeNumber") }}
{{ $datetype = "Downloaded" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "episode.title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "episode.overview" }}
{{ if .Bool "episode.hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ else if eq $type "missing" }}
{{ $date = .String "airDateUtc" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "title" }}
{{ $summary = .String "overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "seasonNumber") (.Int "episodeNumber") }}
{{ $datetype = "Aired" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "overview" }}
{{ if .Bool "episode.hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ else if eq $type "upcoming" }}
{{ $date = .String "airDateUtc" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "title" }}
{{ $summary = .String "overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "seasonNumber") (.Int "episodeNumber") }}
{{ $datetype = "Airs" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "overview" }}
{{ if .Bool "hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ end }}
{{ else if eq $service "radarr" }}
{{ $movie := "" }}
{{ $status = .String "status" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v3/mediacover" $apiBaseUrl }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ end }}
{{ if eq $status "announced"}}
{{ if ne (.String "inCinemas") "" }}
{{ $date = (.String "inCinemas" | parseTime "RFC3339") }}
{{ $datetype = "In Cinemas" }}
{{ else }}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Releases" }}
{{ end }}
{{ else if eq $status "inCinemas"}}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Releases" }}
{{ else if eq $status "released"}}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Released" }}
{{ end }}
{{ if eq $type "recent" }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "movie.id") $key }}
{{ else }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "movie.id") }}
{{ end }}
{{ $datetype = "Downloaded" }}
{{ $date = (.String "date" | parseTime "RFC3339") }}
{{ $title = .String "movie.title" }}
{{ $subtitle = "" }}
{{ $summary = .String "movie.overview" }}
{{ $popoverTitle = .String "movie.title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "movie.overview" }}
{{ $link = printf "%s/movie/%s#" $url (.String "movie.titleSlug") }}
{{ $movie = .Get "movie" }}
{{ $genres = $movie.Get "genres" }}
{{ if and $showGrabbed (gt (.Int "movie.movieFileId") 0) }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "id") $key }}
{{ else }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "id") }}
{{ end }}
{{ $title = .String "title" }}
{{ $subtitle = "" }}
{{ $summary = .String "overview" }}
{{ $link = printf "%s/movie/%s#" $url (.String "titleSlug") }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "overview" }}
{{ $genres = .Get "genres" }}
{{ if eq $type "missing" }}
{{ if and $showGrabbed (.Bool "movie.hasFile") }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ if and $showGrabbed (.Bool "hasFile") }}
{{ $grabbed = true }}
{{ end }}
{{ end }}
{{ end }}
{{ else if eq $service "lidarr" }}
{{ $artist := "" }}
{{ $album := "" }}
{{ $albumId := "" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v1/mediacover" $apiBaseUrl }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ end }}
{{ if eq $type "recent" }}
{{ $album = .Get "album" }}
{{ $artist = $album.Get "artist" }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg?apikey=%s" $coverBase (.String "albumId") $key }}
{{ else }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg" $coverBase (.String "albumId") }}
{{ end }}
{{ $grabbed = true }}
{{ $title = $album.String "title" }}
{{ $date = (.String "date" | parseTime "RFC3339") }}
{{ $datetype = "Downloaded" }}
{{ $subtitle = $artist.String "artistName" }}
{{ $genres = $artist.Get "genres" }}
{{ $link = printf "%s/artist/%s#" $url ($artist.String "foreignArtistId") }}
{{ $summary = $album.String "overview" }}
{{ $popoverTitle = $album.String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = $artist.String "overview" }}
{{ else }}
{{ $artist = .Get "artist" }}
{{ $album = .Get "album" }}
{{ if eq $type "missing" }}
{{ $datetype = "Released" }}
{{ else }}
{{ $datetype = "Releases" }}
{{ end }}
{{ range .Array "releases" }}
{{ $albumId = .String "albumId" }}
{{ break }}
{{ end }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg?apikey=%s" $coverBase $albumId $key }}
{{ else }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg" $coverBase $albumId }}
{{ end }}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $title = .String "title" }}
{{ $subtitle = $artist.String "artistName" }}
{{ $summary = $album.String "overview" }}
{{ $genres = $artist.Get "genres" }}
{{ $link = printf "%s/artist/%s#" $url ($artist.String "foreignArtistId") }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "artist.overview" }}
{{ end }}
{{ end }}
{{ if eq $size "small" }}
{{ $buttonJustify = "right" }}
{{ $height = "9rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "9rem" }}
{{ else }}
{{ $width = "6rem" }}
{{ end }}
{{ else if eq $size "medium" }}
{{ $height = "12rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "12rem" }}
{{ else }}
{{ $width = "8rem" }}
{{ end }}
{{ else if eq $size "large" }}
{{ $height = "15rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "15rem" }}
{{ else }}
{{ $width = "10rem" }}
{{ end }}
{{ else if eq $size "huge" }}
{{ $height = "18rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "18rem" }}
{{ else }}
{{ $width = "12rem" }}
{{ end }}
{{ end }}
<li style="position: relative;">
<div class="flex gap-10 items-start thumbnail-container thumbnail-parent">
<div>
<div data-popover-type="html" data-popover-position="above" data-popover-show-delay="500" style="width: {{ $width }}; height: {{ $height }}; align-content: center;">
<div data-popover-html>
<div style="margin: 5px;">
<strong class="size-h4 color-primary" title="{{ $popoverTitle }}">{{ $popoverTitle }}</strong>
<div class="size-h4 text-truncate text-very-compact color-subdue" title="{{ $popoverSubtitle }}">{{ $popoverSubtitle }}</div>
<p class="margin-top-20" style="overflow-y: auto; text-align: justify; max-height: 20rem;">
{{ if ne $popoverSummary "" }}
{{ $popoverSummary }}
{{ else }}
TBA
{{ end }}
</p>
{{ if gt (len ($genres.Array "")) 0 }}
<ul class="attachments margin-top-20">
{{ range $genres.Array "" }}
<li>{{ .String "" }}</li>
{{ end }}
</ul>
{{ end }}
</div>
</div>
<img class="thumbnail" src="{{ $coverUrl }}" alt="Cover for {{ .String "title" }}" loading="lazy" style="width: 100%; height: 100%; box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); object-fit: cover; border-radius: 0.5rem;">
</div>
</div>
<div class="shrink min-width-0" style="height: 9rem; position: relative; padding-top: 5px; padding-right: 5px;">
<strong class="size-h4 block text-truncate color-primary" title="{{ $title }}">{{ $title }}</strong>
<div class="text-truncate text-very-compact" title="{{ $subtitle }}">{{ $subtitle }}</div>
<div class="text-very-compact text-truncate">
{{ if eq $service "sonarr" }}
<div>{{ $seString }} - {{ $datetype }} {{ $date.Format "1/2 03:04PM" }}</div>
{{ else }}
<span>{{ $datetype }} {{ $date.Format "1/2" }}</span>
{{ end }}
</div>
{{ if $showGrabbed }}
{{ if eq $buttonJustify "right" }}
</div>
<a href="{{ $link }}" class="bookmarks-link size-h4 color-primary" target="_blank" rel="noreferrer"
style="position: absolute; bottom: 1rem; right: 1rem;
{{ if $grabbed }} color: var(--color-positive); border: 1px solid var(--color-positive); {{ else }} color: var(--color-negative); border: 1px solid var(--color-negative); {{ end }}
font-weight: bold; padding: 2px 5px; border-radius: 3px; display: inline-block; margin-top: 5px; text-decoration: none;">
{{ if $grabbed }}Grabbed{{ else }}Missing{{ end }}
</a>
{{ else }}
<a href="{{ $link }}" class="bookmarks-link size-h4 color-primary" target="_blank" rel="noreferrer"
style="{{ if $grabbed }} color: var(--color-positive); border: 1px solid var(--color-positive); {{ else }} color: var(--color-negative); border: 1px solid var(--color-negative); {{ end }}
font-weight: bold; padding: 2px 5px; border-radius: 3px; display: inline-block; margin-top: 5px; text-decoration: none;">
{{ if $grabbed }}Grabbed{{ else }}Missing{{ end }}
</a>
</div>
{{ end }}
{{ else }}
<div class="{{ if eq $size "small" }}text-truncate{{ if eq $service "radarr" }} margin-top-5{{ end }}{{ else }}text-truncate-2-lines margin-top-5{{ end }}">
{{ $summary }}
</div>
{{ end }}
</div>
</li>
{{ end }}
{{ end }}
{{ if not $itemDisplayed }}
<li>No items found.</li>
{{ end }}
</ul>
{{ end }}

View File

@@ -0,0 +1,450 @@
# *arr community widget: Sonarr wanted episodes
- type: custom-api
title: Wanted Episodes
title-url: ${SONARR_URL}
cache: 30m
options:
service: sonarr
type: missing
size: medium
collapse-after: 5
show-grabbed: false
interval: 20
api-base-url: ${SONARR_API_URL}
key: ${SONARR_KEY}
url: ${SONARR_URL}
template: |
{{ $collapseAfter := .Options.IntOr "collapse-after" 5 }}
{{ $showGrabbed := .Options.BoolOr "show-grabbed" false }}
{{ $sortTime := .Options.StringOr "sort-time" "desc" }}
{{ $coverProxy := .Options.StringOr "cover-proxy" "" }}
{{ $size := .Options.StringOr "size" "medium" }}
{{ $service := .Options.StringOr "service" "" }}
{{ $type := .Options.StringOr "type" "" }}
{{ $intervalH := .Options.IntOr "interval" 0 | mul 24 }}
{{ $now := now | formatTime "2006-01-02T15:04:05" }}
{{ $posInterval := (offsetNow (printf "+%dh" $intervalH)) | formatTime "2006-01-02T15:04:05" }}
{{ $negInterval := (offsetNow (printf "-%dh" $intervalH)) | formatTime "2006-01-02T15:04:05" }}
{{ $apiBaseUrl := .Options.StringOr "api-base-url" "" }}
{{ $key := .Options.StringOr "key" "" }}
{{ $url := .Options.StringOr "url" $apiBaseUrl }}
{{ if or (and (ne $service "sonarr") (ne $service "radarr") (ne $service "lidarr"))
(and (ne $type "upcoming") (ne $type "recent") (ne $type "missing"))
(eq $apiBaseUrl "") (eq $key "") (eq $url "") }}
<div class="widget-error-header">
<div class="color-negative size-h3">ERROR</div>
<svg class="widget-error-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"></path>
</svg>
</div>
<p class="break-all">
Some options are not set or malformed
<table style="border-spacing: 1rem;">
<tr><td>service</td><td>{{ $service }}</td><td>must be sonarr, radarr, or lidarr</td></tr>
<tr><td>type</td><td>{{ $type }}</td><td>must be upcoming, recent, or missing</td></tr>
<tr><td>api-base-url</td><td>{{ $apiBaseUrl }}</td><td>should include http(s):// and port if needed</td></tr>
<tr><td>key</td><td>{{ $key }}</td><td></td></tr>
</table>
</p>
{{ else }}
{{ $requestUrl := "" }}
{{ if eq $service "sonarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v3/history/since?includeSeries=true&includeEpisode=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v3/wanted/missing?page=1&pageSize=50&includeSeries=true" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v3/calendar?includeSeries=true&start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ else if eq $service "radarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v3/history/since?includeMovie=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v3/wanted/missing?page=1&pageSize=50" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v3/calendar?start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ else if eq $service "lidarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v1/history/since?includeArtist=true&includeAlbum=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v1/wanted/missing?page=1&pageSize=50&includeArtist=true" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v1/calendar?includeArtist=true&start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ end }}
{{ $data := newRequest $requestUrl
| withHeader "Accept" "application/json"
| withHeader "X-Api-Key" $key
| getResponse }}
<ul class="list list-gap-14 collapsible-container single-line-titles" data-collapse-after="{{ $collapseAfter }}">
{{ $array := "" }}
{{ $sortByField := "" }}
{{ $itemDisplayed := false }}
{{ $itemDate := "" }}
{{ $isAvailable := false }}
{{ if eq $type "missing" }}
{{ $array = "records" }}
{{ if eq $service "sonarr" }}
{{ $sortByField = "airDateUtc" }}
{{ else }}
{{ $sortByField = "releaseDate" }}
{{ end }}
{{ else if eq $type "recent" }}
{{ $sortByField = "date" }}
{{ else }}
{{ if eq $service "sonarr" }}
{{ $sortByField = "airDateUtc" }}
{{ else }}
{{ $sortByField = "releaseDate" }}
{{ end }}
{{ end }}
{{ range $data.JSON.Array $array | sortByTime $sortByField "rfc3339" $sortTime }}
{{ if eq $service "sonarr" }}
{{ $itemDate = .String "airDateUtc" | parseTime "RFC3339" }}
{{ $itemDate = $itemDate.In now.Location | formatTime "2006-01-02T15:04:05" }}
{{ $isAvailable = true }}
{{ else if eq $service "radarr"}}
{{ $isAvailable = .Bool "isAvailable" }}
{{ $itemDate = .String "releaseDate" }}
{{ else if eq $service "lidarr"}}
{{ $itemDate = .String "releaseDate" }}
{{ $isAvailable = true }}
{{ end }}
{{ if or (eq $type "upcoming") (eq $type "recent") (and (or (and (gt $itemDate $negInterval) ((lt $itemDate $now ))) (eq $intervalH 0)) $isAvailable) }}
{{ $itemDisplayed = true }}
{{ $title := "" }}
{{ $subtitle := "" }}
{{ $coverUrl := "" }}
{{ $status := "" }}
{{ $coverBase := "" }}
{{ $height := "" }}
{{ $width := "" }}
{{ $popoverTitle := "" }}
{{ $popoverSubtitle := "" }}
{{ $popoverSummary := "" }}
{{ $summary := "" }}
{{ $link := "" }}
{{ $grabbed := false }}
{{ $date := now }}
{{ $datetype := "" }}
{{ $seString := "" }}
{{ $genres := "" }}
{{ $buttonJustify := "left" }}
{{ if eq $service "sonarr" }}
{{ $series := "" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v3/mediacover" $apiBaseUrl }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "seriesId") $key }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "seriesId") }}
{{ end }}
{{ $title = .String "series.title" }}
{{ $link = printf "%s/series/%s#" $url (.String "series.titleSlug") }}
{{ $series = .Get "series" }}
{{ $genres = $series.Get "genres" }}
{{ if eq $type "recent" }}
{{ $date = .String "date" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "episode.title" }}
{{ $summary = .String "episode.overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "episode.seasonNumber") (.Int "episode.episodeNumber") }}
{{ $datetype = "Downloaded" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "episode.title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "episode.overview" }}
{{ if .Bool "episode.hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ else if eq $type "missing" }}
{{ $date = .String "airDateUtc" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "title" }}
{{ $summary = .String "overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "seasonNumber") (.Int "episodeNumber") }}
{{ $datetype = "Aired" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "overview" }}
{{ if .Bool "episode.hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ else if eq $type "upcoming" }}
{{ $date = .String "airDateUtc" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "title" }}
{{ $summary = .String "overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "seasonNumber") (.Int "episodeNumber") }}
{{ $datetype = "Airs" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "overview" }}
{{ if .Bool "hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ end }}
{{ else if eq $service "radarr" }}
{{ $movie := "" }}
{{ $status = .String "status" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v3/mediacover" $apiBaseUrl }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ end }}
{{ if eq $status "announced"}}
{{ if ne (.String "inCinemas") "" }}
{{ $date = (.String "inCinemas" | parseTime "RFC3339") }}
{{ $datetype = "In Cinemas" }}
{{ else }}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Releases" }}
{{ end }}
{{ else if eq $status "inCinemas"}}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Releases" }}
{{ else if eq $status "released"}}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Released" }}
{{ end }}
{{ if eq $type "recent" }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "movie.id") $key }}
{{ else }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "movie.id") }}
{{ end }}
{{ $datetype = "Downloaded" }}
{{ $date = (.String "date" | parseTime "RFC3339") }}
{{ $title = .String "movie.title" }}
{{ $subtitle = "" }}
{{ $summary = .String "movie.overview" }}
{{ $popoverTitle = .String "movie.title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "movie.overview" }}
{{ $link = printf "%s/movie/%s#" $url (.String "movie.titleSlug") }}
{{ $movie = .Get "movie" }}
{{ $genres = $movie.Get "genres" }}
{{ if and $showGrabbed (gt (.Int "movie.movieFileId") 0) }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "id") $key }}
{{ else }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "id") }}
{{ end }}
{{ $title = .String "title" }}
{{ $subtitle = "" }}
{{ $summary = .String "overview" }}
{{ $link = printf "%s/movie/%s#" $url (.String "titleSlug") }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "overview" }}
{{ $genres = .Get "genres" }}
{{ if eq $type "missing" }}
{{ if and $showGrabbed (.Bool "movie.hasFile") }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ if and $showGrabbed (.Bool "hasFile") }}
{{ $grabbed = true }}
{{ end }}
{{ end }}
{{ end }}
{{ else if eq $service "lidarr" }}
{{ $artist := "" }}
{{ $album := "" }}
{{ $albumId := "" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v1/mediacover" $apiBaseUrl }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ end }}
{{ if eq $type "recent" }}
{{ $album = .Get "album" }}
{{ $artist = $album.Get "artist" }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg?apikey=%s" $coverBase (.String "albumId") $key }}
{{ else }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg" $coverBase (.String "albumId") }}
{{ end }}
{{ $grabbed = true }}
{{ $title = $album.String "title" }}
{{ $date = (.String "date" | parseTime "RFC3339") }}
{{ $datetype = "Downloaded" }}
{{ $subtitle = $artist.String "artistName" }}
{{ $genres = $artist.Get "genres" }}
{{ $link = printf "%s/artist/%s#" $url ($artist.String "foreignArtistId") }}
{{ $summary = $album.String "overview" }}
{{ $popoverTitle = $album.String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = $artist.String "overview" }}
{{ else }}
{{ $artist = .Get "artist" }}
{{ $album = .Get "album" }}
{{ if eq $type "missing" }}
{{ $datetype = "Released" }}
{{ else }}
{{ $datetype = "Releases" }}
{{ end }}
{{ range .Array "releases" }}
{{ $albumId = .String "albumId" }}
{{ break }}
{{ end }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg?apikey=%s" $coverBase $albumId $key }}
{{ else }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg" $coverBase $albumId }}
{{ end }}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $title = .String "title" }}
{{ $subtitle = $artist.String "artistName" }}
{{ $summary = $album.String "overview" }}
{{ $genres = $artist.Get "genres" }}
{{ $link = printf "%s/artist/%s#" $url ($artist.String "foreignArtistId") }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "artist.overview" }}
{{ end }}
{{ end }}
{{ if eq $size "small" }}
{{ $buttonJustify = "right" }}
{{ $height = "9rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "9rem" }}
{{ else }}
{{ $width = "6rem" }}
{{ end }}
{{ else if eq $size "medium" }}
{{ $height = "12rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "12rem" }}
{{ else }}
{{ $width = "8rem" }}
{{ end }}
{{ else if eq $size "large" }}
{{ $height = "15rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "15rem" }}
{{ else }}
{{ $width = "10rem" }}
{{ end }}
{{ else if eq $size "huge" }}
{{ $height = "18rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "18rem" }}
{{ else }}
{{ $width = "12rem" }}
{{ end }}
{{ end }}
<li style="position: relative;">
<div class="flex gap-10 items-start thumbnail-container thumbnail-parent">
<div>
<div data-popover-type="html" data-popover-position="above" data-popover-show-delay="500" style="width: {{ $width }}; height: {{ $height }}; align-content: center;">
<div data-popover-html>
<div style="margin: 5px;">
<strong class="size-h4 color-primary" title="{{ $popoverTitle }}">{{ $popoverTitle }}</strong>
<div class="size-h4 text-truncate text-very-compact color-subdue" title="{{ $popoverSubtitle }}">{{ $popoverSubtitle }}</div>
<p class="margin-top-20" style="overflow-y: auto; text-align: justify; max-height: 20rem;">
{{ if ne $popoverSummary "" }}
{{ $popoverSummary }}
{{ else }}
TBA
{{ end }}
</p>
{{ if gt (len ($genres.Array "")) 0 }}
<ul class="attachments margin-top-20">
{{ range $genres.Array "" }}
<li>{{ .String "" }}</li>
{{ end }}
</ul>
{{ end }}
</div>
</div>
<img class="thumbnail" src="{{ $coverUrl }}" alt="Cover for {{ .String "title" }}" loading="lazy" style="width: 100%; height: 100%; box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); object-fit: cover; border-radius: 0.5rem;">
</div>
</div>
<div class="shrink min-width-0" style="height: 9rem; position: relative; padding-top: 5px; padding-right: 5px;">
<strong class="size-h4 block text-truncate color-primary" title="{{ $title }}">{{ $title }}</strong>
<div class="text-truncate text-very-compact" title="{{ $subtitle }}">{{ $subtitle }}</div>
<div class="text-very-compact text-truncate">
{{ if eq $service "sonarr" }}
<div>{{ $seString }} - {{ $datetype }} {{ $date.Format "1/2 03:04PM" }}</div>
{{ else }}
<span>{{ $datetype }} {{ $date.Format "1/2" }}</span>
{{ end }}
</div>
{{ if $showGrabbed }}
{{ if eq $buttonJustify "right" }}
</div>
<a href="{{ $link }}" class="bookmarks-link size-h4 color-primary" target="_blank" rel="noreferrer"
style="position: absolute; bottom: 1rem; right: 1rem;
{{ if $grabbed }} color: var(--color-positive); border: 1px solid var(--color-positive); {{ else }} color: var(--color-negative); border: 1px solid var(--color-negative); {{ end }}
font-weight: bold; padding: 2px 5px; border-radius: 3px; display: inline-block; margin-top: 5px; text-decoration: none;">
{{ if $grabbed }}Grabbed{{ else }}Missing{{ end }}
</a>
{{ else }}
<a href="{{ $link }}" class="bookmarks-link size-h4 color-primary" target="_blank" rel="noreferrer"
style="{{ if $grabbed }} color: var(--color-positive); border: 1px solid var(--color-positive); {{ else }} color: var(--color-negative); border: 1px solid var(--color-negative); {{ end }}
font-weight: bold; padding: 2px 5px; border-radius: 3px; display: inline-block; margin-top: 5px; text-decoration: none;">
{{ if $grabbed }}Grabbed{{ else }}Missing{{ end }}
</a>
</div>
{{ end }}
{{ else }}
<div class="{{ if eq $size "small" }}text-truncate{{ if eq $service "radarr" }} margin-top-5{{ end }}{{ else }}text-truncate-2-lines margin-top-5{{ end }}">
{{ $summary }}
</div>
{{ end }}
</div>
</li>
{{ end }}
{{ end }}
{{ if not $itemDisplayed }}
<li>No items found.</li>
{{ end }}
</ul>
{{ end }}

View File

@@ -0,0 +1,450 @@
# *arr community widget: Sonarr recent episodes
- type: custom-api
title: Recent Episodes
title-url: ${SONARR_URL}
cache: 30m
options:
service: sonarr
type: recent
size: medium
collapse-after: 5
show-grabbed: false
interval: 20
api-base-url: ${SONARR_API_URL}
key: ${SONARR_KEY}
url: ${SONARR_URL}
template: |
{{ $collapseAfter := .Options.IntOr "collapse-after" 5 }}
{{ $showGrabbed := .Options.BoolOr "show-grabbed" false }}
{{ $sortTime := .Options.StringOr "sort-time" "desc" }}
{{ $coverProxy := .Options.StringOr "cover-proxy" "" }}
{{ $size := .Options.StringOr "size" "medium" }}
{{ $service := .Options.StringOr "service" "" }}
{{ $type := .Options.StringOr "type" "" }}
{{ $intervalH := .Options.IntOr "interval" 0 | mul 24 }}
{{ $now := now | formatTime "2006-01-02T15:04:05" }}
{{ $posInterval := (offsetNow (printf "+%dh" $intervalH)) | formatTime "2006-01-02T15:04:05" }}
{{ $negInterval := (offsetNow (printf "-%dh" $intervalH)) | formatTime "2006-01-02T15:04:05" }}
{{ $apiBaseUrl := .Options.StringOr "api-base-url" "" }}
{{ $key := .Options.StringOr "key" "" }}
{{ $url := .Options.StringOr "url" $apiBaseUrl }}
{{ if or (and (ne $service "sonarr") (ne $service "radarr") (ne $service "lidarr"))
(and (ne $type "upcoming") (ne $type "recent") (ne $type "missing"))
(eq $apiBaseUrl "") (eq $key "") (eq $url "") }}
<div class="widget-error-header">
<div class="color-negative size-h3">ERROR</div>
<svg class="widget-error-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"></path>
</svg>
</div>
<p class="break-all">
Some options are not set or malformed
<table style="border-spacing: 1rem;">
<tr><td>service</td><td>{{ $service }}</td><td>must be sonarr, radarr, or lidarr</td></tr>
<tr><td>type</td><td>{{ $type }}</td><td>must be upcoming, recent, or missing</td></tr>
<tr><td>api-base-url</td><td>{{ $apiBaseUrl }}</td><td>should include http(s):// and port if needed</td></tr>
<tr><td>key</td><td>{{ $key }}</td><td></td></tr>
</table>
</p>
{{ else }}
{{ $requestUrl := "" }}
{{ if eq $service "sonarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v3/history/since?includeSeries=true&includeEpisode=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v3/wanted/missing?page=1&pageSize=50&includeSeries=true" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v3/calendar?includeSeries=true&start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ else if eq $service "radarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v3/history/since?includeMovie=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v3/wanted/missing?page=1&pageSize=50" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v3/calendar?start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ else if eq $service "lidarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v1/history/since?includeArtist=true&includeAlbum=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v1/wanted/missing?page=1&pageSize=50&includeArtist=true" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v1/calendar?includeArtist=true&start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ end }}
{{ $data := newRequest $requestUrl
| withHeader "Accept" "application/json"
| withHeader "X-Api-Key" $key
| getResponse }}
<ul class="list list-gap-14 collapsible-container single-line-titles" data-collapse-after="{{ $collapseAfter }}">
{{ $array := "" }}
{{ $sortByField := "" }}
{{ $itemDisplayed := false }}
{{ $itemDate := "" }}
{{ $isAvailable := false }}
{{ if eq $type "missing" }}
{{ $array = "records" }}
{{ if eq $service "sonarr" }}
{{ $sortByField = "airDateUtc" }}
{{ else }}
{{ $sortByField = "releaseDate" }}
{{ end }}
{{ else if eq $type "recent" }}
{{ $sortByField = "date" }}
{{ else }}
{{ if eq $service "sonarr" }}
{{ $sortByField = "airDateUtc" }}
{{ else }}
{{ $sortByField = "releaseDate" }}
{{ end }}
{{ end }}
{{ range $data.JSON.Array $array | sortByTime $sortByField "rfc3339" $sortTime }}
{{ if eq $service "sonarr" }}
{{ $itemDate = .String "airDateUtc" | parseTime "RFC3339" }}
{{ $itemDate = $itemDate.In now.Location | formatTime "2006-01-02T15:04:05" }}
{{ $isAvailable = true }}
{{ else if eq $service "radarr"}}
{{ $isAvailable = .Bool "isAvailable" }}
{{ $itemDate = .String "releaseDate" }}
{{ else if eq $service "lidarr"}}
{{ $itemDate = .String "releaseDate" }}
{{ $isAvailable = true }}
{{ end }}
{{ if or (eq $type "upcoming") (eq $type "recent") (and (or (and (gt $itemDate $negInterval) ((lt $itemDate $now ))) (eq $intervalH 0)) $isAvailable) }}
{{ $itemDisplayed = true }}
{{ $title := "" }}
{{ $subtitle := "" }}
{{ $coverUrl := "" }}
{{ $status := "" }}
{{ $coverBase := "" }}
{{ $height := "" }}
{{ $width := "" }}
{{ $popoverTitle := "" }}
{{ $popoverSubtitle := "" }}
{{ $popoverSummary := "" }}
{{ $summary := "" }}
{{ $link := "" }}
{{ $grabbed := false }}
{{ $date := now }}
{{ $datetype := "" }}
{{ $seString := "" }}
{{ $genres := "" }}
{{ $buttonJustify := "left" }}
{{ if eq $service "sonarr" }}
{{ $series := "" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v3/mediacover" $apiBaseUrl }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "seriesId") $key }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "seriesId") }}
{{ end }}
{{ $title = .String "series.title" }}
{{ $link = printf "%s/series/%s#" $url (.String "series.titleSlug") }}
{{ $series = .Get "series" }}
{{ $genres = $series.Get "genres" }}
{{ if eq $type "recent" }}
{{ $date = .String "date" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "episode.title" }}
{{ $summary = .String "episode.overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "episode.seasonNumber") (.Int "episode.episodeNumber") }}
{{ $datetype = "Downloaded" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "episode.title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "episode.overview" }}
{{ if .Bool "episode.hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ else if eq $type "missing" }}
{{ $date = .String "airDateUtc" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "title" }}
{{ $summary = .String "overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "seasonNumber") (.Int "episodeNumber") }}
{{ $datetype = "Aired" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "overview" }}
{{ if .Bool "episode.hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ else if eq $type "upcoming" }}
{{ $date = .String "airDateUtc" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "title" }}
{{ $summary = .String "overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "seasonNumber") (.Int "episodeNumber") }}
{{ $datetype = "Airs" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "overview" }}
{{ if .Bool "hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ end }}
{{ else if eq $service "radarr" }}
{{ $movie := "" }}
{{ $status = .String "status" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v3/mediacover" $apiBaseUrl }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ end }}
{{ if eq $status "announced"}}
{{ if ne (.String "inCinemas") "" }}
{{ $date = (.String "inCinemas" | parseTime "RFC3339") }}
{{ $datetype = "In Cinemas" }}
{{ else }}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Releases" }}
{{ end }}
{{ else if eq $status "inCinemas"}}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Releases" }}
{{ else if eq $status "released"}}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Released" }}
{{ end }}
{{ if eq $type "recent" }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "movie.id") $key }}
{{ else }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "movie.id") }}
{{ end }}
{{ $datetype = "Downloaded" }}
{{ $date = (.String "date" | parseTime "RFC3339") }}
{{ $title = .String "movie.title" }}
{{ $subtitle = "" }}
{{ $summary = .String "movie.overview" }}
{{ $popoverTitle = .String "movie.title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "movie.overview" }}
{{ $link = printf "%s/movie/%s#" $url (.String "movie.titleSlug") }}
{{ $movie = .Get "movie" }}
{{ $genres = $movie.Get "genres" }}
{{ if and $showGrabbed (gt (.Int "movie.movieFileId") 0) }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "id") $key }}
{{ else }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "id") }}
{{ end }}
{{ $title = .String "title" }}
{{ $subtitle = "" }}
{{ $summary = .String "overview" }}
{{ $link = printf "%s/movie/%s#" $url (.String "titleSlug") }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "overview" }}
{{ $genres = .Get "genres" }}
{{ if eq $type "missing" }}
{{ if and $showGrabbed (.Bool "movie.hasFile") }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ if and $showGrabbed (.Bool "hasFile") }}
{{ $grabbed = true }}
{{ end }}
{{ end }}
{{ end }}
{{ else if eq $service "lidarr" }}
{{ $artist := "" }}
{{ $album := "" }}
{{ $albumId := "" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v1/mediacover" $apiBaseUrl }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ end }}
{{ if eq $type "recent" }}
{{ $album = .Get "album" }}
{{ $artist = $album.Get "artist" }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg?apikey=%s" $coverBase (.String "albumId") $key }}
{{ else }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg" $coverBase (.String "albumId") }}
{{ end }}
{{ $grabbed = true }}
{{ $title = $album.String "title" }}
{{ $date = (.String "date" | parseTime "RFC3339") }}
{{ $datetype = "Downloaded" }}
{{ $subtitle = $artist.String "artistName" }}
{{ $genres = $artist.Get "genres" }}
{{ $link = printf "%s/artist/%s#" $url ($artist.String "foreignArtistId") }}
{{ $summary = $album.String "overview" }}
{{ $popoverTitle = $album.String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = $artist.String "overview" }}
{{ else }}
{{ $artist = .Get "artist" }}
{{ $album = .Get "album" }}
{{ if eq $type "missing" }}
{{ $datetype = "Released" }}
{{ else }}
{{ $datetype = "Releases" }}
{{ end }}
{{ range .Array "releases" }}
{{ $albumId = .String "albumId" }}
{{ break }}
{{ end }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg?apikey=%s" $coverBase $albumId $key }}
{{ else }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg" $coverBase $albumId }}
{{ end }}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $title = .String "title" }}
{{ $subtitle = $artist.String "artistName" }}
{{ $summary = $album.String "overview" }}
{{ $genres = $artist.Get "genres" }}
{{ $link = printf "%s/artist/%s#" $url ($artist.String "foreignArtistId") }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "artist.overview" }}
{{ end }}
{{ end }}
{{ if eq $size "small" }}
{{ $buttonJustify = "right" }}
{{ $height = "9rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "9rem" }}
{{ else }}
{{ $width = "6rem" }}
{{ end }}
{{ else if eq $size "medium" }}
{{ $height = "12rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "12rem" }}
{{ else }}
{{ $width = "8rem" }}
{{ end }}
{{ else if eq $size "large" }}
{{ $height = "15rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "15rem" }}
{{ else }}
{{ $width = "10rem" }}
{{ end }}
{{ else if eq $size "huge" }}
{{ $height = "18rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "18rem" }}
{{ else }}
{{ $width = "12rem" }}
{{ end }}
{{ end }}
<li style="position: relative;">
<div class="flex gap-10 items-start thumbnail-container thumbnail-parent">
<div>
<div data-popover-type="html" data-popover-position="above" data-popover-show-delay="500" style="width: {{ $width }}; height: {{ $height }}; align-content: center;">
<div data-popover-html>
<div style="margin: 5px;">
<strong class="size-h4 color-primary" title="{{ $popoverTitle }}">{{ $popoverTitle }}</strong>
<div class="size-h4 text-truncate text-very-compact color-subdue" title="{{ $popoverSubtitle }}">{{ $popoverSubtitle }}</div>
<p class="margin-top-20" style="overflow-y: auto; text-align: justify; max-height: 20rem;">
{{ if ne $popoverSummary "" }}
{{ $popoverSummary }}
{{ else }}
TBA
{{ end }}
</p>
{{ if gt (len ($genres.Array "")) 0 }}
<ul class="attachments margin-top-20">
{{ range $genres.Array "" }}
<li>{{ .String "" }}</li>
{{ end }}
</ul>
{{ end }}
</div>
</div>
<img class="thumbnail" src="{{ $coverUrl }}" alt="Cover for {{ .String "title" }}" loading="lazy" style="width: 100%; height: 100%; box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); object-fit: cover; border-radius: 0.5rem;">
</div>
</div>
<div class="shrink min-width-0" style="height: 9rem; position: relative; padding-top: 5px; padding-right: 5px;">
<strong class="size-h4 block text-truncate color-primary" title="{{ $title }}">{{ $title }}</strong>
<div class="text-truncate text-very-compact" title="{{ $subtitle }}">{{ $subtitle }}</div>
<div class="text-very-compact text-truncate">
{{ if eq $service "sonarr" }}
<div>{{ $seString }} - {{ $datetype }} {{ $date.Format "1/2 03:04PM" }}</div>
{{ else }}
<span>{{ $datetype }} {{ $date.Format "1/2" }}</span>
{{ end }}
</div>
{{ if $showGrabbed }}
{{ if eq $buttonJustify "right" }}
</div>
<a href="{{ $link }}" class="bookmarks-link size-h4 color-primary" target="_blank" rel="noreferrer"
style="position: absolute; bottom: 1rem; right: 1rem;
{{ if $grabbed }} color: var(--color-positive); border: 1px solid var(--color-positive); {{ else }} color: var(--color-negative); border: 1px solid var(--color-negative); {{ end }}
font-weight: bold; padding: 2px 5px; border-radius: 3px; display: inline-block; margin-top: 5px; text-decoration: none;">
{{ if $grabbed }}Grabbed{{ else }}Missing{{ end }}
</a>
{{ else }}
<a href="{{ $link }}" class="bookmarks-link size-h4 color-primary" target="_blank" rel="noreferrer"
style="{{ if $grabbed }} color: var(--color-positive); border: 1px solid var(--color-positive); {{ else }} color: var(--color-negative); border: 1px solid var(--color-negative); {{ end }}
font-weight: bold; padding: 2px 5px; border-radius: 3px; display: inline-block; margin-top: 5px; text-decoration: none;">
{{ if $grabbed }}Grabbed{{ else }}Missing{{ end }}
</a>
</div>
{{ end }}
{{ else }}
<div class="{{ if eq $size "small" }}text-truncate{{ if eq $service "radarr" }} margin-top-5{{ end }}{{ else }}text-truncate-2-lines margin-top-5{{ end }}">
{{ $summary }}
</div>
{{ end }}
</div>
</li>
{{ end }}
{{ end }}
{{ if not $itemDisplayed }}
<li>No items found.</li>
{{ end }}
</ul>
{{ end }}

View File

@@ -0,0 +1,450 @@
# *arr community widget: Sonarr upcoming episodes
- type: custom-api
title: Upcoming Episodes
title-url: ${SONARR_URL}
cache: 30m
options:
service: sonarr
type: upcoming
size: medium
collapse-after: 5
show-grabbed: false
interval: 20
api-base-url: ${SONARR_API_URL}
key: ${SONARR_KEY}
url: ${SONARR_URL}
template: |
{{ $collapseAfter := .Options.IntOr "collapse-after" 5 }}
{{ $showGrabbed := .Options.BoolOr "show-grabbed" false }}
{{ $sortTime := .Options.StringOr "sort-time" "desc" }}
{{ $coverProxy := .Options.StringOr "cover-proxy" "" }}
{{ $size := .Options.StringOr "size" "medium" }}
{{ $service := .Options.StringOr "service" "" }}
{{ $type := .Options.StringOr "type" "" }}
{{ $intervalH := .Options.IntOr "interval" 0 | mul 24 }}
{{ $now := now | formatTime "2006-01-02T15:04:05" }}
{{ $posInterval := (offsetNow (printf "+%dh" $intervalH)) | formatTime "2006-01-02T15:04:05" }}
{{ $negInterval := (offsetNow (printf "-%dh" $intervalH)) | formatTime "2006-01-02T15:04:05" }}
{{ $apiBaseUrl := .Options.StringOr "api-base-url" "" }}
{{ $key := .Options.StringOr "key" "" }}
{{ $url := .Options.StringOr "url" $apiBaseUrl }}
{{ if or (and (ne $service "sonarr") (ne $service "radarr") (ne $service "lidarr"))
(and (ne $type "upcoming") (ne $type "recent") (ne $type "missing"))
(eq $apiBaseUrl "") (eq $key "") (eq $url "") }}
<div class="widget-error-header">
<div class="color-negative size-h3">ERROR</div>
<svg class="widget-error-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"></path>
</svg>
</div>
<p class="break-all">
Some options are not set or malformed
<table style="border-spacing: 1rem;">
<tr><td>service</td><td>{{ $service }}</td><td>must be sonarr, radarr, or lidarr</td></tr>
<tr><td>type</td><td>{{ $type }}</td><td>must be upcoming, recent, or missing</td></tr>
<tr><td>api-base-url</td><td>{{ $apiBaseUrl }}</td><td>should include http(s):// and port if needed</td></tr>
<tr><td>key</td><td>{{ $key }}</td><td></td></tr>
</table>
</p>
{{ else }}
{{ $requestUrl := "" }}
{{ if eq $service "sonarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v3/history/since?includeSeries=true&includeEpisode=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v3/wanted/missing?page=1&pageSize=50&includeSeries=true" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v3/calendar?includeSeries=true&start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ else if eq $service "radarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v3/history/since?includeMovie=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v3/wanted/missing?page=1&pageSize=50" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v3/calendar?start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ else if eq $service "lidarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v1/history/since?includeArtist=true&includeAlbum=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v1/wanted/missing?page=1&pageSize=50&includeArtist=true" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v1/calendar?includeArtist=true&start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ end }}
{{ $data := newRequest $requestUrl
| withHeader "Accept" "application/json"
| withHeader "X-Api-Key" $key
| getResponse }}
<ul class="list list-gap-14 collapsible-container single-line-titles" data-collapse-after="{{ $collapseAfter }}">
{{ $array := "" }}
{{ $sortByField := "" }}
{{ $itemDisplayed := false }}
{{ $itemDate := "" }}
{{ $isAvailable := false }}
{{ if eq $type "missing" }}
{{ $array = "records" }}
{{ if eq $service "sonarr" }}
{{ $sortByField = "airDateUtc" }}
{{ else }}
{{ $sortByField = "releaseDate" }}
{{ end }}
{{ else if eq $type "recent" }}
{{ $sortByField = "date" }}
{{ else }}
{{ if eq $service "sonarr" }}
{{ $sortByField = "airDateUtc" }}
{{ else }}
{{ $sortByField = "releaseDate" }}
{{ end }}
{{ end }}
{{ range $data.JSON.Array $array | sortByTime $sortByField "rfc3339" $sortTime }}
{{ if eq $service "sonarr" }}
{{ $itemDate = .String "airDateUtc" | parseTime "RFC3339" }}
{{ $itemDate = $itemDate.In now.Location | formatTime "2006-01-02T15:04:05" }}
{{ $isAvailable = true }}
{{ else if eq $service "radarr"}}
{{ $isAvailable = .Bool "isAvailable" }}
{{ $itemDate = .String "releaseDate" }}
{{ else if eq $service "lidarr"}}
{{ $itemDate = .String "releaseDate" }}
{{ $isAvailable = true }}
{{ end }}
{{ if or (eq $type "upcoming") (eq $type "recent") (and (or (and (gt $itemDate $negInterval) ((lt $itemDate $now ))) (eq $intervalH 0)) $isAvailable) }}
{{ $itemDisplayed = true }}
{{ $title := "" }}
{{ $subtitle := "" }}
{{ $coverUrl := "" }}
{{ $status := "" }}
{{ $coverBase := "" }}
{{ $height := "" }}
{{ $width := "" }}
{{ $popoverTitle := "" }}
{{ $popoverSubtitle := "" }}
{{ $popoverSummary := "" }}
{{ $summary := "" }}
{{ $link := "" }}
{{ $grabbed := false }}
{{ $date := now }}
{{ $datetype := "" }}
{{ $seString := "" }}
{{ $genres := "" }}
{{ $buttonJustify := "left" }}
{{ if eq $service "sonarr" }}
{{ $series := "" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v3/mediacover" $apiBaseUrl }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "seriesId") $key }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "seriesId") }}
{{ end }}
{{ $title = .String "series.title" }}
{{ $link = printf "%s/series/%s#" $url (.String "series.titleSlug") }}
{{ $series = .Get "series" }}
{{ $genres = $series.Get "genres" }}
{{ if eq $type "recent" }}
{{ $date = .String "date" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "episode.title" }}
{{ $summary = .String "episode.overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "episode.seasonNumber") (.Int "episode.episodeNumber") }}
{{ $datetype = "Downloaded" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "episode.title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "episode.overview" }}
{{ if .Bool "episode.hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ else if eq $type "missing" }}
{{ $date = .String "airDateUtc" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "title" }}
{{ $summary = .String "overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "seasonNumber") (.Int "episodeNumber") }}
{{ $datetype = "Aired" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "overview" }}
{{ if .Bool "episode.hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ else if eq $type "upcoming" }}
{{ $date = .String "airDateUtc" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "title" }}
{{ $summary = .String "overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "seasonNumber") (.Int "episodeNumber") }}
{{ $datetype = "Airs" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "overview" }}
{{ if .Bool "hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ end }}
{{ else if eq $service "radarr" }}
{{ $movie := "" }}
{{ $status = .String "status" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v3/mediacover" $apiBaseUrl }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ end }}
{{ if eq $status "announced"}}
{{ if ne (.String "inCinemas") "" }}
{{ $date = (.String "inCinemas" | parseTime "RFC3339") }}
{{ $datetype = "In Cinemas" }}
{{ else }}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Releases" }}
{{ end }}
{{ else if eq $status "inCinemas"}}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Releases" }}
{{ else if eq $status "released"}}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Released" }}
{{ end }}
{{ if eq $type "recent" }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "movie.id") $key }}
{{ else }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "movie.id") }}
{{ end }}
{{ $datetype = "Downloaded" }}
{{ $date = (.String "date" | parseTime "RFC3339") }}
{{ $title = .String "movie.title" }}
{{ $subtitle = "" }}
{{ $summary = .String "movie.overview" }}
{{ $popoverTitle = .String "movie.title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "movie.overview" }}
{{ $link = printf "%s/movie/%s#" $url (.String "movie.titleSlug") }}
{{ $movie = .Get "movie" }}
{{ $genres = $movie.Get "genres" }}
{{ if and $showGrabbed (gt (.Int "movie.movieFileId") 0) }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "id") $key }}
{{ else }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "id") }}
{{ end }}
{{ $title = .String "title" }}
{{ $subtitle = "" }}
{{ $summary = .String "overview" }}
{{ $link = printf "%s/movie/%s#" $url (.String "titleSlug") }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "overview" }}
{{ $genres = .Get "genres" }}
{{ if eq $type "missing" }}
{{ if and $showGrabbed (.Bool "movie.hasFile") }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ if and $showGrabbed (.Bool "hasFile") }}
{{ $grabbed = true }}
{{ end }}
{{ end }}
{{ end }}
{{ else if eq $service "lidarr" }}
{{ $artist := "" }}
{{ $album := "" }}
{{ $albumId := "" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v1/mediacover" $apiBaseUrl }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ end }}
{{ if eq $type "recent" }}
{{ $album = .Get "album" }}
{{ $artist = $album.Get "artist" }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg?apikey=%s" $coverBase (.String "albumId") $key }}
{{ else }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg" $coverBase (.String "albumId") }}
{{ end }}
{{ $grabbed = true }}
{{ $title = $album.String "title" }}
{{ $date = (.String "date" | parseTime "RFC3339") }}
{{ $datetype = "Downloaded" }}
{{ $subtitle = $artist.String "artistName" }}
{{ $genres = $artist.Get "genres" }}
{{ $link = printf "%s/artist/%s#" $url ($artist.String "foreignArtistId") }}
{{ $summary = $album.String "overview" }}
{{ $popoverTitle = $album.String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = $artist.String "overview" }}
{{ else }}
{{ $artist = .Get "artist" }}
{{ $album = .Get "album" }}
{{ if eq $type "missing" }}
{{ $datetype = "Released" }}
{{ else }}
{{ $datetype = "Releases" }}
{{ end }}
{{ range .Array "releases" }}
{{ $albumId = .String "albumId" }}
{{ break }}
{{ end }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg?apikey=%s" $coverBase $albumId $key }}
{{ else }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg" $coverBase $albumId }}
{{ end }}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $title = .String "title" }}
{{ $subtitle = $artist.String "artistName" }}
{{ $summary = $album.String "overview" }}
{{ $genres = $artist.Get "genres" }}
{{ $link = printf "%s/artist/%s#" $url ($artist.String "foreignArtistId") }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "artist.overview" }}
{{ end }}
{{ end }}
{{ if eq $size "small" }}
{{ $buttonJustify = "right" }}
{{ $height = "9rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "9rem" }}
{{ else }}
{{ $width = "6rem" }}
{{ end }}
{{ else if eq $size "medium" }}
{{ $height = "12rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "12rem" }}
{{ else }}
{{ $width = "8rem" }}
{{ end }}
{{ else if eq $size "large" }}
{{ $height = "15rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "15rem" }}
{{ else }}
{{ $width = "10rem" }}
{{ end }}
{{ else if eq $size "huge" }}
{{ $height = "18rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "18rem" }}
{{ else }}
{{ $width = "12rem" }}
{{ end }}
{{ end }}
<li style="position: relative;">
<div class="flex gap-10 items-start thumbnail-container thumbnail-parent">
<div>
<div data-popover-type="html" data-popover-position="above" data-popover-show-delay="500" style="width: {{ $width }}; height: {{ $height }}; align-content: center;">
<div data-popover-html>
<div style="margin: 5px;">
<strong class="size-h4 color-primary" title="{{ $popoverTitle }}">{{ $popoverTitle }}</strong>
<div class="size-h4 text-truncate text-very-compact color-subdue" title="{{ $popoverSubtitle }}">{{ $popoverSubtitle }}</div>
<p class="margin-top-20" style="overflow-y: auto; text-align: justify; max-height: 20rem;">
{{ if ne $popoverSummary "" }}
{{ $popoverSummary }}
{{ else }}
TBA
{{ end }}
</p>
{{ if gt (len ($genres.Array "")) 0 }}
<ul class="attachments margin-top-20">
{{ range $genres.Array "" }}
<li>{{ .String "" }}</li>
{{ end }}
</ul>
{{ end }}
</div>
</div>
<img class="thumbnail" src="{{ $coverUrl }}" alt="Cover for {{ .String "title" }}" loading="lazy" style="width: 100%; height: 100%; box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); object-fit: cover; border-radius: 0.5rem;">
</div>
</div>
<div class="shrink min-width-0" style="height: 9rem; position: relative; padding-top: 5px; padding-right: 5px;">
<strong class="size-h4 block text-truncate color-primary" title="{{ $title }}">{{ $title }}</strong>
<div class="text-truncate text-very-compact" title="{{ $subtitle }}">{{ $subtitle }}</div>
<div class="text-very-compact text-truncate">
{{ if eq $service "sonarr" }}
<div>{{ $seString }} - {{ $datetype }} {{ $date.Format "1/2 03:04PM" }}</div>
{{ else }}
<span>{{ $datetype }} {{ $date.Format "1/2" }}</span>
{{ end }}
</div>
{{ if $showGrabbed }}
{{ if eq $buttonJustify "right" }}
</div>
<a href="{{ $link }}" class="bookmarks-link size-h4 color-primary" target="_blank" rel="noreferrer"
style="position: absolute; bottom: 1rem; right: 1rem;
{{ if $grabbed }} color: var(--color-positive); border: 1px solid var(--color-positive); {{ else }} color: var(--color-negative); border: 1px solid var(--color-negative); {{ end }}
font-weight: bold; padding: 2px 5px; border-radius: 3px; display: inline-block; margin-top: 5px; text-decoration: none;">
{{ if $grabbed }}Grabbed{{ else }}Missing{{ end }}
</a>
{{ else }}
<a href="{{ $link }}" class="bookmarks-link size-h4 color-primary" target="_blank" rel="noreferrer"
style="{{ if $grabbed }} color: var(--color-positive); border: 1px solid var(--color-positive); {{ else }} color: var(--color-negative); border: 1px solid var(--color-negative); {{ end }}
font-weight: bold; padding: 2px 5px; border-radius: 3px; display: inline-block; margin-top: 5px; text-decoration: none;">
{{ if $grabbed }}Grabbed{{ else }}Missing{{ end }}
</a>
</div>
{{ end }}
{{ else }}
<div class="{{ if eq $size "small" }}text-truncate{{ if eq $service "radarr" }} margin-top-5{{ end }}{{ else }}text-truncate-2-lines margin-top-5{{ end }}">
{{ $summary }}
</div>
{{ end }}
</div>
</li>
{{ end }}
{{ end }}
{{ if not $itemDisplayed }}
<li>No items found.</li>
{{ end }}
</ul>
{{ end }}

View File

@@ -0,0 +1,433 @@
{{ $collapseAfter := .Options.IntOr "collapse-after" 5 }}
{{ $showGrabbed := .Options.BoolOr "show-grabbed" false }}
{{ $sortTime := .Options.StringOr "sort-time" "desc" }}
{{ $coverProxy := .Options.StringOr "cover-proxy" "" }}
{{ $size := .Options.StringOr "size" "medium" }}
{{ $service := .Options.StringOr "service" "" }}
{{ $type := .Options.StringOr "type" "" }}
{{ $intervalH := .Options.IntOr "interval" 0 | mul 24 }}
{{ $now := now | formatTime "2006-01-02T15:04:05" }}
{{ $posInterval := (offsetNow (printf "+%dh" $intervalH)) | formatTime "2006-01-02T15:04:05" }}
{{ $negInterval := (offsetNow (printf "-%dh" $intervalH)) | formatTime "2006-01-02T15:04:05" }}
{{ $apiBaseUrl := .Options.StringOr "api-base-url" "" }}
{{ $key := .Options.StringOr "key" "" }}
{{ $url := .Options.StringOr "url" $apiBaseUrl }}
{{ if or (and (ne $service "sonarr") (ne $service "radarr") (ne $service "lidarr"))
(and (ne $type "upcoming") (ne $type "recent") (ne $type "missing"))
(eq $apiBaseUrl "") (eq $key "") (eq $url "") }}
<div class="widget-error-header">
<div class="color-negative size-h3">ERROR</div>
<svg class="widget-error-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"></path>
</svg>
</div>
<p class="break-all">
Some options are not set or malformed
<table style="border-spacing: 1rem;">
<tr><td>service</td><td>{{ $service }}</td><td>must be sonarr, radarr, or lidarr</td></tr>
<tr><td>type</td><td>{{ $type }}</td><td>must be upcoming, recent, or missing</td></tr>
<tr><td>api-base-url</td><td>{{ $apiBaseUrl }}</td><td>should include http(s):// and port if needed</td></tr>
<tr><td>key</td><td>{{ $key }}</td><td></td></tr>
</table>
</p>
{{ else }}
{{ $requestUrl := "" }}
{{ if eq $service "sonarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v3/history/since?includeSeries=true&includeEpisode=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v3/wanted/missing?page=1&pageSize=50&includeSeries=true" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v3/calendar?includeSeries=true&start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ else if eq $service "radarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v3/history/since?includeMovie=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v3/wanted/missing?page=1&pageSize=50" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v3/calendar?start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ else if eq $service "lidarr" }}
{{ if eq $type "recent" }}
{{ $requestUrl = printf "%s/api/v1/history/since?includeArtist=true&includeAlbum=true&eventType=grabbed&date=%s" $apiBaseUrl $negInterval }}
{{ else if eq $type "missing" }}
{{ $requestUrl = printf "%s/api/v1/wanted/missing?page=1&pageSize=50&includeArtist=true" $apiBaseUrl }}
{{ else if eq $type "upcoming" }}
{{ $requestUrl = printf "%s/api/v1/calendar?includeArtist=true&start=%s&end=%s" $apiBaseUrl $now $posInterval }}
{{ end }}
{{ end }}
{{ $data := newRequest $requestUrl
| withHeader "Accept" "application/json"
| withHeader "X-Api-Key" $key
| getResponse }}
<ul class="list list-gap-14 collapsible-container single-line-titles" data-collapse-after="{{ $collapseAfter }}">
{{ $array := "" }}
{{ $sortByField := "" }}
{{ $itemDisplayed := false }}
{{ $itemDate := "" }}
{{ $isAvailable := false }}
{{ if eq $type "missing" }}
{{ $array = "records" }}
{{ if eq $service "sonarr" }}
{{ $sortByField = "airDateUtc" }}
{{ else }}
{{ $sortByField = "releaseDate" }}
{{ end }}
{{ else if eq $type "recent" }}
{{ $sortByField = "date" }}
{{ else }}
{{ if eq $service "sonarr" }}
{{ $sortByField = "airDateUtc" }}
{{ else }}
{{ $sortByField = "releaseDate" }}
{{ end }}
{{ end }}
{{ range $data.JSON.Array $array | sortByTime $sortByField "rfc3339" $sortTime }}
{{ if eq $service "sonarr" }}
{{ $itemDate = .String "airDateUtc" | parseTime "RFC3339" }}
{{ $itemDate = $itemDate.In now.Location | formatTime "2006-01-02T15:04:05" }}
{{ $isAvailable = true }}
{{ else if eq $service "radarr"}}
{{ $isAvailable = .Bool "isAvailable" }}
{{ $itemDate = .String "releaseDate" }}
{{ else if eq $service "lidarr"}}
{{ $itemDate = .String "releaseDate" }}
{{ $isAvailable = true }}
{{ end }}
{{ if or (eq $type "upcoming") (eq $type "recent") (and (or (and (gt $itemDate $negInterval) ((lt $itemDate $now ))) (eq $intervalH 0)) $isAvailable) }}
{{ $itemDisplayed = true }}
{{ $title := "" }}
{{ $subtitle := "" }}
{{ $coverUrl := "" }}
{{ $status := "" }}
{{ $coverBase := "" }}
{{ $height := "" }}
{{ $width := "" }}
{{ $popoverTitle := "" }}
{{ $popoverSubtitle := "" }}
{{ $popoverSummary := "" }}
{{ $summary := "" }}
{{ $link := "" }}
{{ $grabbed := false }}
{{ $date := now }}
{{ $datetype := "" }}
{{ $seString := "" }}
{{ $genres := "" }}
{{ $buttonJustify := "left" }}
{{ if eq $service "sonarr" }}
{{ $series := "" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v3/mediacover" $apiBaseUrl }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "seriesId") $key }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "seriesId") }}
{{ end }}
{{ $title = .String "series.title" }}
{{ $link = printf "%s/series/%s#" $url (.String "series.titleSlug") }}
{{ $series = .Get "series" }}
{{ $genres = $series.Get "genres" }}
{{ if eq $type "recent" }}
{{ $date = .String "date" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "episode.title" }}
{{ $summary = .String "episode.overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "episode.seasonNumber") (.Int "episode.episodeNumber") }}
{{ $datetype = "Downloaded" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "episode.title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "episode.overview" }}
{{ if .Bool "episode.hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ else if eq $type "missing" }}
{{ $date = .String "airDateUtc" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "title" }}
{{ $summary = .String "overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "seasonNumber") (.Int "episodeNumber") }}
{{ $datetype = "Aired" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "overview" }}
{{ if .Bool "episode.hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ else if eq $type "upcoming" }}
{{ $date = .String "airDateUtc" | parseLocalTime "RFC3339" }}
{{ $date = $date.In now.Location }}
{{ $subtitle = .String "title" }}
{{ $summary = .String "overview" }}
{{ $seString = printf "S%02dE%02d" (.Int "seasonNumber") (.Int "episodeNumber") }}
{{ $datetype = "Airs" }}
{{ if $showGrabbed }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = $seString }}
{{ $popoverSummary = .String "overview" }}
{{ if .Bool "hasFile" }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ $popoverTitle = .String "series.title" }}
{{ $popoverSummary = .String "series.overview" }}
{{ end }}
{{ end }}
{{ else if eq $service "radarr" }}
{{ $movie := "" }}
{{ $status = .String "status" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v3/mediacover" $apiBaseUrl }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ end }}
{{ if eq $status "announced"}}
{{ if ne (.String "inCinemas") "" }}
{{ $date = (.String "inCinemas" | parseTime "RFC3339") }}
{{ $datetype = "In Cinemas" }}
{{ else }}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Releases" }}
{{ end }}
{{ else if eq $status "inCinemas"}}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Releases" }}
{{ else if eq $status "released"}}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $datetype = "Released" }}
{{ end }}
{{ if eq $type "recent" }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "movie.id") $key }}
{{ else }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "movie.id") }}
{{ end }}
{{ $datetype = "Downloaded" }}
{{ $date = (.String "date" | parseTime "RFC3339") }}
{{ $title = .String "movie.title" }}
{{ $subtitle = "" }}
{{ $summary = .String "movie.overview" }}
{{ $popoverTitle = .String "movie.title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "movie.overview" }}
{{ $link = printf "%s/movie/%s#" $url (.String "movie.titleSlug") }}
{{ $movie = .Get "movie" }}
{{ $genres = $movie.Get "genres" }}
{{ if and $showGrabbed (gt (.Int "movie.movieFileId") 0) }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg?apikey=%s" $coverBase (.String "id") $key }}
{{ else }}
{{ $coverUrl = printf "%s/%s/poster-500.jpg" $coverBase (.String "id") }}
{{ end }}
{{ $title = .String "title" }}
{{ $subtitle = "" }}
{{ $summary = .String "overview" }}
{{ $link = printf "%s/movie/%s#" $url (.String "titleSlug") }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "overview" }}
{{ $genres = .Get "genres" }}
{{ if eq $type "missing" }}
{{ if and $showGrabbed (.Bool "movie.hasFile") }}
{{ $grabbed = true }}
{{ end }}
{{ else }}
{{ if and $showGrabbed (.Bool "hasFile") }}
{{ $grabbed = true }}
{{ end }}
{{ end }}
{{ end }}
{{ else if eq $service "lidarr" }}
{{ $artist := "" }}
{{ $album := "" }}
{{ $albumId := "" }}
{{ if eq $coverProxy "" }}
{{ $coverBase = printf "%s/api/v1/mediacover" $apiBaseUrl }}
{{ else }}
{{ $coverBase = $coverProxy }}
{{ end }}
{{ if eq $type "recent" }}
{{ $album = .Get "album" }}
{{ $artist = $album.Get "artist" }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg?apikey=%s" $coverBase (.String "albumId") $key }}
{{ else }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg" $coverBase (.String "albumId") }}
{{ end }}
{{ $grabbed = true }}
{{ $title = $album.String "title" }}
{{ $date = (.String "date" | parseTime "RFC3339") }}
{{ $datetype = "Downloaded" }}
{{ $subtitle = $artist.String "artistName" }}
{{ $genres = $artist.Get "genres" }}
{{ $link = printf "%s/artist/%s#" $url ($artist.String "foreignArtistId") }}
{{ $summary = $album.String "overview" }}
{{ $popoverTitle = $album.String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = $artist.String "overview" }}
{{ else }}
{{ $artist = .Get "artist" }}
{{ $album = .Get "album" }}
{{ if eq $type "missing" }}
{{ $datetype = "Released" }}
{{ else }}
{{ $datetype = "Releases" }}
{{ end }}
{{ range .Array "releases" }}
{{ $albumId = .String "albumId" }}
{{ break }}
{{ end }}
{{ if eq $coverProxy "" }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg?apikey=%s" $coverBase $albumId $key }}
{{ else }}
{{ $coverUrl = printf "%s/album/%s/cover-500.jpg" $coverBase $albumId }}
{{ end }}
{{ $date = (.String "releaseDate" | parseTime "RFC3339") }}
{{ $title = .String "title" }}
{{ $subtitle = $artist.String "artistName" }}
{{ $summary = $album.String "overview" }}
{{ $genres = $artist.Get "genres" }}
{{ $link = printf "%s/artist/%s#" $url ($artist.String "foreignArtistId") }}
{{ $popoverTitle = .String "title" }}
{{ $popoverSubtitle = printf "%s %s" $datetype ($date | formatTime "1/2/2006") }}
{{ $popoverSummary = .String "artist.overview" }}
{{ end }}
{{ end }}
{{ if eq $size "small" }}
{{ $buttonJustify = "right" }}
{{ $height = "9rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "9rem" }}
{{ else }}
{{ $width = "6rem" }}
{{ end }}
{{ else if eq $size "medium" }}
{{ $height = "12rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "12rem" }}
{{ else }}
{{ $width = "8rem" }}
{{ end }}
{{ else if eq $size "large" }}
{{ $height = "15rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "15rem" }}
{{ else }}
{{ $width = "10rem" }}
{{ end }}
{{ else if eq $size "huge" }}
{{ $height = "18rem" }}
{{ if eq $service "lidarr" }}
{{ $width = "18rem" }}
{{ else }}
{{ $width = "12rem" }}
{{ end }}
{{ end }}
<li style="position: relative;">
<div class="flex gap-10 items-start thumbnail-container thumbnail-parent">
<div>
<div data-popover-type="html" data-popover-position="above" data-popover-show-delay="500" style="width: {{ $width }}; height: {{ $height }}; align-content: center;">
<div data-popover-html>
<div style="margin: 5px;">
<strong class="size-h4 color-primary" title="{{ $popoverTitle }}">{{ $popoverTitle }}</strong>
<div class="size-h4 text-truncate text-very-compact color-subdue" title="{{ $popoverSubtitle }}">{{ $popoverSubtitle }}</div>
<p class="margin-top-20" style="overflow-y: auto; text-align: justify; max-height: 20rem;">
{{ if ne $popoverSummary "" }}
{{ $popoverSummary }}
{{ else }}
TBA
{{ end }}
</p>
{{ if gt (len ($genres.Array "")) 0 }}
<ul class="attachments margin-top-20">
{{ range $genres.Array "" }}
<li>{{ .String "" }}</li>
{{ end }}
</ul>
{{ end }}
</div>
</div>
<img class="thumbnail" src="{{ $coverUrl }}" alt="Cover for {{ .String "title" }}" loading="lazy" style="width: 100%; height: 100%; box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); object-fit: cover; border-radius: 0.5rem;">
</div>
</div>
<div class="shrink min-width-0" style="height: 9rem; position: relative; padding-top: 5px; padding-right: 5px;">
<strong class="size-h4 block text-truncate color-primary" title="{{ $title }}">{{ $title }}</strong>
<div class="text-truncate text-very-compact" title="{{ $subtitle }}">{{ $subtitle }}</div>
<div class="text-very-compact text-truncate">
{{ if eq $service "sonarr" }}
<div>{{ $seString }} - {{ $datetype }} {{ $date.Format "1/2 03:04PM" }}</div>
{{ else }}
<span>{{ $datetype }} {{ $date.Format "1/2" }}</span>
{{ end }}
</div>
{{ if $showGrabbed }}
{{ if eq $buttonJustify "right" }}
</div>
<a href="{{ $link }}" class="bookmarks-link size-h4 color-primary" target="_blank" rel="noreferrer"
style="position: absolute; bottom: 1rem; right: 1rem;
{{ if $grabbed }} color: var(--color-positive); border: 1px solid var(--color-positive); {{ else }} color: var(--color-negative); border: 1px solid var(--color-negative); {{ end }}
font-weight: bold; padding: 2px 5px; border-radius: 3px; display: inline-block; margin-top: 5px; text-decoration: none;">
{{ if $grabbed }}Grabbed{{ else }}Missing{{ end }}
</a>
{{ else }}
<a href="{{ $link }}" class="bookmarks-link size-h4 color-primary" target="_blank" rel="noreferrer"
style="{{ if $grabbed }} color: var(--color-positive); border: 1px solid var(--color-positive); {{ else }} color: var(--color-negative); border: 1px solid var(--color-negative); {{ end }}
font-weight: bold; padding: 2px 5px; border-radius: 3px; display: inline-block; margin-top: 5px; text-decoration: none;">
{{ if $grabbed }}Grabbed{{ else }}Missing{{ end }}
</a>
</div>
{{ end }}
{{ else }}
<div class="{{ if eq $size "small" }}text-truncate{{ if eq $service "radarr" }} margin-top-5{{ end }}{{ else }}text-truncate-2-lines margin-top-5{{ end }}">
{{ $summary }}
</div>
{{ end }}
</div>
</li>
{{ end }}
{{ end }}
{{ if not $itemDisplayed }}
<li>No items found.</li>
{{ end }}
</ul>
{{ end }}

View File

@@ -0,0 +1,27 @@
- type: monitor
title: Media
sites:
- title: Jellyfin
url: https://jellyfin.home
icon: mdi:television-play
- title: Jellyseerr
url: https://jellyseerr.home
icon: mdi:movie-search
- title: Radarr
url: https://radarr.home
icon: mdi:film
- title: Sonarr
url: https://sonarr.home
icon: mdi:television
- title: Lidarr
url: https://lidarr.home
icon: mdi:music
- title: Prowlarr
url: https://prowlarr.home
icon: mdi:radar
- title: qBittorrent
url: https://qbit.home
icon: mdi:download
- title: TorrServe
url: https://torrserve.home
icon: mdi:server-network

View File

@@ -0,0 +1,10 @@
apiVersion: 1
providers:
- name: homelab
folder: Homelab
type: file
disableDeletion: false
editable: true
options:
path: /etc/grafana/provisioning/dashboards/json

View File

@@ -0,0 +1,16 @@
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: false
editable: true
- name: Loki
type: loki
access: proxy
url: http://loki:3100
isDefault: true
editable: true

View File

@@ -0,0 +1,49 @@
auth_enabled: false
server:
http_listen_port: 3100
log_level: info
ingester:
wal:
dir: /loki/wal
lifecycler:
ring:
kvstore:
store: inmemory
replication_factor: 1
chunk_idle_period: 5m
chunk_target_size: 1048576
max_transfer_retries: 0
schema_config:
configs:
- from: 2020-10-24
store: boltdb
object_store: filesystem
schema: v11
index:
prefix: index_
period: 168h
storage_config:
boltdb:
directory: /loki/index
filesystem:
directory: /loki/chunks
limits_config:
enforce_metric_name: false
reject_old_samples: true
reject_old_samples_max_age: 168h
split_queries_by_interval: 15m
querier:
max_outstanding_per_tenant: 4096
chunk_store_config:
max_look_back_period: 0s
table_manager:
retention_deletes_enabled: true
retention_period: 168h

View File

@@ -0,0 +1,24 @@
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: "node"
static_configs:
- targets: ["192.168.1.70:9100"]
- job_name: "cadvisor"
static_configs:
- targets: ["cadvisor:8080"]
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
- job_name: "caddy"
scheme: https
metrics_path: /metrics
tls_config:
insecure_skip_verify: true
static_configs:
- targets: ["metrics.home:443"]

View File

@@ -0,0 +1,48 @@
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: varlogs
static_configs:
- targets: [localhost]
labels:
job: varlogs
host: cyberdeck
__path__: /var/log/**/*.log
- job_name: docker
static_configs:
- targets: [localhost]
labels:
job: docker
host: cyberdeck
__path__: /var/lib/docker/containers/*/*-json.log
pipeline_stages:
- docker: {}
relabel_configs:
- source_labels: [__path__]
regex: "/var/lib/docker/containers/([^/]+)/.*"
target_label: container
replacement: "$1"
- job_name: journald
journal:
path: /var/log/journal
max_age: 12h
labels:
job: journald
host: cyberdeck
relabel_configs:
- source_labels: [__journal__systemd_unit]
target_label: unit
- source_labels: [__journal__hostname]
target_label: host
- source_labels: [__journal__priority_keyword]
target_label: level