Skip to content
This repository was archived by the owner on Aug 3, 2024. It is now read-only.

Commit ff17077

Browse files
m-rotsvoltron4lyfel3uddz
authored
feat(triggers): readarr (#174)
Co-authored-by: voltron4lyfe <55123373+voltron4lyfe@users.noreply.github.com> Co-authored-by: l3uddz <l3uddz@gmail.com>
1 parent 63edb8e commit ff17077

9 files changed

Lines changed: 313 additions & 40 deletions

File tree

.github/workflows/build.yml

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@ on:
66
- '*'
77
tags:
88
- 'v*'
9+
pull_request:
10+
branches:
11+
- master
912

1013
jobs:
1114
build:
1215
runs-on: ubuntu-latest
1316
steps:
1417
# dependencies
1518
- name: goreleaser
16-
uses: goreleaser/goreleaser-action@v2
19+
uses: goreleaser/goreleaser-action@v3
1720
with:
1821
install-only: true
1922
version: 1.7.0
@@ -28,20 +31,22 @@ jobs:
2831
run: task --version
2932

3033
- name: qemu
31-
uses: docker/setup-qemu-action@v1
34+
if: github.event.pull_request.head.repo.fork == false
35+
uses: docker/setup-qemu-action@v2
3236

3337
- name: buildx
34-
uses: docker/setup-buildx-action@v1
38+
if: github.event.pull_request.head.repo.fork == false
39+
uses: docker/setup-buildx-action@v2
3540

3641
# checkout
3742
- name: checkout
38-
uses: actions/checkout@v2
43+
uses: actions/checkout@v3
3944
with:
4045
fetch-depth: 0
4146

4247
# setup go
4348
- name: go
44-
uses: actions/setup-go@v1
49+
uses: actions/setup-go@v3
4550
with:
4651
go-version: 1.19
4752

@@ -51,26 +56,18 @@ jobs:
5156
go env
5257
5358
# cache
54-
- name: cache-paths
55-
id: go-cache-paths
56-
run: |
57-
echo "::set-output name=go-build::$(go env GOCACHE)"
58-
echo "::set-output name=go-mod::$(go env GOMODCACHE)"
59-
60-
- name: cache-build
61-
uses: actions/cache@v2
62-
with:
63-
path: ${{ steps.go-cache-paths.outputs.go-build }}
64-
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}
65-
66-
- name: cache-mod
67-
uses: actions/cache@v2
59+
- name: cache-go
60+
uses: actions/cache@v3
6861
with:
69-
path: ${{ steps.go-cache-paths.outputs.go-mod }}
70-
key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
62+
path: |
63+
~/.cache/go-build
64+
~/go/pkg/mod
65+
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
66+
restore-keys: |
67+
${{ runner.os }}-go-cache-mod
7168
7269
- name: cache-task
73-
uses: actions/cache@v2
70+
uses: actions/cache@v3
7471
with:
7572
path: .task/**/*
7673
key: ${{ runner.os }}-go-task
@@ -106,19 +103,20 @@ jobs:
106103
107104
# artifacts
108105
- name: artifact_linux
109-
uses: actions/upload-artifact@v2-preview
106+
uses: actions/upload-artifact@v3
110107
with:
111108
name: build_linux
112109
path: dist/*linux*
113110

114111
- name: artifact_darwin
115-
uses: actions/upload-artifact@v2-preview
112+
uses: actions/upload-artifact@v3
116113
with:
117114
name: build_darwin
118115
path: dist/*darwin*
119116

120117
# docker login
121118
- name: docker login
119+
if: github.event.pull_request.head.repo.fork == false
122120
env:
123121
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
124122
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
@@ -127,15 +125,15 @@ jobs:
127125
128126
# docker build (latest & tag)
129127
- name: release tag
130-
if: startsWith(github.ref, 'refs/tags/') == true
128+
if: startsWith(github.ref, 'refs/tags/') == true && github.event.pull_request.head.repo.fork == false
131129
uses: little-core-labs/get-git-tag@v3.0.2
132130
id: releasetag
133131
with:
134132
tagRegex: "v?(.+)"
135133

136134
- name: docker - build release
137-
if: startsWith(github.ref, 'refs/tags/') == true
138-
uses: docker/build-push-action@v2
135+
if: startsWith(github.ref, 'refs/tags/') == true && github.event.pull_request.head.repo.fork == false
136+
uses: docker/build-push-action@v3
139137
with:
140138
context: .
141139
file: ./docker/Dockerfile
@@ -150,13 +148,13 @@ jobs:
150148
151149
# docker build (branch)
152150
- name: branch name
153-
if: startsWith(github.ref, 'refs/tags/') == false
151+
if: startsWith(github.ref, 'refs/tags/') == false && github.event.pull_request.head.repo.fork == false
154152
id: branch-name
155-
uses: tj-actions/branch-names@v2.2
153+
uses: tj-actions/branch-names@v6.2
156154

157155
- name: docker tag
158-
if: startsWith(github.ref, 'refs/tags/') == false
159-
uses: frabert/replace-string-action@master
156+
if: startsWith(github.ref, 'refs/tags/') == false && github.event.pull_request.head.repo.fork == false
157+
uses: frabert/replace-string-action@v2.3
160158
id: dockertag
161159
with:
162160
pattern: '[:\.\/]+'
@@ -165,8 +163,8 @@ jobs:
165163
flags: 'g'
166164

167165
- name: docker - build branch
168-
if: startsWith(github.ref, 'refs/tags/') == false
169-
uses: docker/build-push-action@v2
166+
if: startsWith(github.ref, 'refs/tags/') == false && github.event.pull_request.head.repo.fork == false
167+
uses: docker/build-push-action@v3
170168
with:
171169
context: .
172170
file: ./docker/Dockerfile
@@ -180,5 +178,6 @@ jobs:
180178
181179
# cleanup
182180
- name: cleanup
181+
if: github.event.pull_request.head.repo.fork == false
183182
run: |
184183
rm -f ${HOME}/.docker/config.json

README.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Autoscan
22

33
Autoscan replaces the default Plex and Emby behaviour for picking up file changes on the file system.
4-
Autoscan integrates with Sonarr, Radarr, Lidarr and Google Drive to fetch changes in near real-time without relying on the file system.
4+
Autoscan integrates with Sonarr, Radarr, Readarr, Lidarr and Google Drive to fetch changes in near real-time without relying on the file system.
55

66
Wait, what happened to [Plex Autoscan](https://github.com/l3uddz/plex_autoscan)?
77
Well, Autoscan is a rewrite of the original Plex Autoscan written in the Go language.
@@ -61,7 +61,7 @@ It is important that all three modules can have access to a file. When a trigger
6161

6262
#### Simple example
6363

64-
- Sonarr running in a Docker container (same example works for Lidarr and Radarr)
64+
- Sonarr running in a Docker container (same example works for Lidarr, Radarr and Readarr)
6565
- Autoscan running on the host OS (not in a container)
6666
- Plex running in a Docker container
6767

@@ -117,8 +117,8 @@ Autoscan currently supports the following triggers:
117117

118118
- Manual: When you want to scan a path manually.
119119

120-
- The -arrs: Lidarr, Sonarr and Radarr. \
121-
Webhook support for Lidarr, Sonarrr and Radarr.
120+
- The -arrs: Lidarr, Sonarr, Radarr and Readarr. \
121+
Webhook support for Lidarr, Sonarr, Radarr and Readarr.
122122

123123
All triggers support:
124124

@@ -180,13 +180,14 @@ The following -arrs are currently provided by Autoscan:
180180

181181
- Lidarr
182182
- Radarr
183+
- Readarr
183184
- Sonarr
184185

185186
#### Connecting the -arrs
186187

187-
To add your webhook to Sonarr, Radarr or Lidarr, do:
188+
To add your webhook to Sonarr, Radarr, Readarr or Lidarr, do:
188189

189-
1. Open the `settings` page in Sonarr/Radarr/Lidarr
190+
1. Open the `settings` page in Sonarr/Radarr/Readarr/Lidarr
190191
2. Select the tab `connect`
191192
3. Click on the big plus sign
192193
4. Select `webhook`
@@ -267,6 +268,10 @@ triggers:
267268
- name: radarr4k # /triggers/radarr4k
268269
priority: 5
269270
271+
readarr:
272+
- name: readarr # /triggers/readarr
273+
priority: 1
274+
270275
sonarr:
271276
- name: sonarr-docker # /triggers/sonarr-docker
272277
priority: 2
@@ -478,6 +483,10 @@ triggers:
478483
- name: radarr4k # /triggers/radarr4k
479484
priority: 5
480485
486+
readarr:
487+
- name: readarr # /triggers/readarr
488+
priority: 1
489+
481490
sonarr:
482491
- name: sonarr-docker # /triggers/sonarr-docker
483492
priority: 2

cmd/autoscan/main.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/cloudbox/autoscan/triggers/lidarr"
3131
"github.com/cloudbox/autoscan/triggers/manual"
3232
"github.com/cloudbox/autoscan/triggers/radarr"
33+
"github.com/cloudbox/autoscan/triggers/readarr"
3334
"github.com/cloudbox/autoscan/triggers/sonarr"
3435

3536
// sqlite3 driver
@@ -59,6 +60,7 @@ type config struct {
5960
Inotify []inotify.Config `yaml:"inotify"`
6061
Lidarr []lidarr.Config `yaml:"lidarr"`
6162
Radarr []radarr.Config `yaml:"radarr"`
63+
Readarr []readarr.Config `yaml:"readarr"`
6264
Sonarr []sonarr.Config `yaml:"sonarr"`
6365
} `yaml:"triggers"`
6466

@@ -268,8 +270,9 @@ func main() {
268270
Int("bernard", len(c.Triggers.Bernard)).
269271
Int("inotify", len(c.Triggers.Inotify)).
270272
Int("lidarr", len(c.Triggers.Lidarr)).
271-
Int("sonarr", len(c.Triggers.Sonarr)).
272273
Int("radarr", len(c.Triggers.Radarr)).
274+
Int("readarr", len(c.Triggers.Readarr)).
275+
Int("sonarr", len(c.Triggers.Sonarr)).
273276
Msg("Initialised triggers")
274277

275278
// targets

cmd/autoscan/router.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/cloudbox/autoscan/triggers/lidarr"
1717
"github.com/cloudbox/autoscan/triggers/manual"
1818
"github.com/cloudbox/autoscan/triggers/radarr"
19+
"github.com/cloudbox/autoscan/triggers/readarr"
1920
"github.com/cloudbox/autoscan/triggers/sonarr"
2021
)
2122

@@ -96,6 +97,15 @@ func getRouter(c config, proc *processor.Processor) chi.Router {
9697
r.Post(pattern(t.Name), trigger(proc.Add).ServeHTTP)
9798
}
9899

100+
for _, t := range c.Triggers.Readarr {
101+
trigger, err := readarr.New(t)
102+
if err != nil {
103+
log.Fatal().Err(err).Str("trigger", t.Name).Msg("Failed initialising trigger")
104+
}
105+
106+
r.Post(pattern(t.Name), trigger(proc.Add).ServeHTTP)
107+
}
108+
99109
for _, t := range c.Triggers.Sonarr {
100110
trigger, err := sonarr.New(t)
101111
if err != nil {

triggers/readarr/readarr.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package readarr
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
"path"
7+
"strings"
8+
"time"
9+
10+
"github.com/rs/zerolog/hlog"
11+
12+
"github.com/cloudbox/autoscan"
13+
)
14+
15+
type Config struct {
16+
Name string `yaml:"name"`
17+
Priority int `yaml:"priority"`
18+
Rewrite []autoscan.Rewrite `yaml:"rewrite"`
19+
Verbosity string `yaml:"verbosity"`
20+
}
21+
22+
// New creates an autoscan-compatible HTTP Trigger for Readarr webhooks.
23+
func New(c Config) (autoscan.HTTPTrigger, error) {
24+
rewriter, err := autoscan.NewRewriter(c.Rewrite)
25+
if err != nil {
26+
return nil, err
27+
}
28+
29+
trigger := func(callback autoscan.ProcessorFunc) http.Handler {
30+
return handler{
31+
callback: callback,
32+
priority: c.Priority,
33+
rewrite: rewriter,
34+
}
35+
}
36+
37+
return trigger, nil
38+
}
39+
40+
type handler struct {
41+
priority int
42+
rewrite autoscan.Rewriter
43+
callback autoscan.ProcessorFunc
44+
}
45+
46+
type readarrEvent struct {
47+
Type string `json:"eventType"`
48+
Upgrade bool `json:"isUpgrade"`
49+
50+
Files []struct {
51+
Path string
52+
} `json:"bookFiles"`
53+
}
54+
55+
func (h handler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
56+
var err error
57+
l := hlog.FromRequest(r)
58+
59+
event := new(readarrEvent)
60+
err = json.NewDecoder(r.Body).Decode(event)
61+
if err != nil {
62+
l.Error().Err(err).Msg("Failed decoding request")
63+
rw.WriteHeader(http.StatusBadRequest)
64+
return
65+
}
66+
67+
l.Trace().Interface("event", event).Msg("Received JSON body")
68+
69+
if strings.EqualFold(event.Type, "Test") {
70+
l.Info().Msg("Received test event")
71+
rw.WriteHeader(http.StatusOK)
72+
return
73+
}
74+
75+
//Only handle test and download. Everything else is ignored.
76+
if !strings.EqualFold(event.Type, "Download") || len(event.Files) == 0 {
77+
l.Error().Msg("Required fields are missing")
78+
rw.WriteHeader(http.StatusBadRequest)
79+
return
80+
}
81+
82+
unique := make(map[string]bool)
83+
scans := make([]autoscan.Scan, 0)
84+
85+
for _, f := range event.Files {
86+
folderPath := path.Dir(h.rewrite(f.Path))
87+
if _, ok := unique[folderPath]; ok {
88+
continue
89+
}
90+
91+
// add scan
92+
unique[folderPath] = true
93+
scans = append(scans, autoscan.Scan{
94+
Folder: folderPath,
95+
Priority: h.priority,
96+
Time: now(),
97+
})
98+
}
99+
100+
err = h.callback(scans...)
101+
if err != nil {
102+
l.Error().Err(err).Msg("Processor could not process scans")
103+
rw.WriteHeader(http.StatusInternalServerError)
104+
return
105+
}
106+
107+
rw.WriteHeader(http.StatusOK)
108+
l.Info().
109+
Str("path", scans[0].Folder).
110+
Str("event", event.Type).
111+
Msg("Scan moved to processor")
112+
}
113+
114+
var now = time.Now

0 commit comments

Comments
 (0)