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

Commit

Permalink
feat(triggers): readarr (#174)
Browse files Browse the repository at this point in the history
Co-authored-by: voltron4lyfe <[email protected]>
Co-authored-by: l3uddz <[email protected]>
  • Loading branch information
3 people authored Nov 24, 2022
1 parent 63edb8e commit ff17077
Show file tree
Hide file tree
Showing 9 changed files with 313 additions and 40 deletions.
65 changes: 32 additions & 33 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ on:
- '*'
tags:
- 'v*'
pull_request:
branches:
- master

jobs:
build:
runs-on: ubuntu-latest
steps:
# dependencies
- name: goreleaser
uses: goreleaser/goreleaser-action@v2
uses: goreleaser/goreleaser-action@v3
with:
install-only: true
version: 1.7.0
Expand All @@ -28,20 +31,22 @@ jobs:
run: task --version

- name: qemu
uses: docker/setup-qemu-action@v1
if: github.event.pull_request.head.repo.fork == false
uses: docker/setup-qemu-action@v2

- name: buildx
uses: docker/setup-buildx-action@v1
if: github.event.pull_request.head.repo.fork == false
uses: docker/setup-buildx-action@v2

# checkout
- name: checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 0

# setup go
- name: go
uses: actions/setup-go@v1
uses: actions/setup-go@v3
with:
go-version: 1.19

Expand All @@ -51,26 +56,18 @@ jobs:
go env
# cache
- name: cache-paths
id: go-cache-paths
run: |
echo "::set-output name=go-build::$(go env GOCACHE)"
echo "::set-output name=go-mod::$(go env GOMODCACHE)"
- name: cache-build
uses: actions/cache@v2
with:
path: ${{ steps.go-cache-paths.outputs.go-build }}
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}

- name: cache-mod
uses: actions/cache@v2
- name: cache-go
uses: actions/cache@v3
with:
path: ${{ steps.go-cache-paths.outputs.go-mod }}
key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-cache-mod
- name: cache-task
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: .task/**/*
key: ${{ runner.os }}-go-task
Expand Down Expand Up @@ -106,19 +103,20 @@ jobs:
# artifacts
- name: artifact_linux
uses: actions/upload-artifact@v2-preview
uses: actions/upload-artifact@v3
with:
name: build_linux
path: dist/*linux*

- name: artifact_darwin
uses: actions/upload-artifact@v2-preview
uses: actions/upload-artifact@v3
with:
name: build_darwin
path: dist/*darwin*

# docker login
- name: docker login
if: github.event.pull_request.head.repo.fork == false
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
Expand All @@ -127,15 +125,15 @@ jobs:
# docker build (latest & tag)
- name: release tag
if: startsWith(github.ref, 'refs/tags/') == true
if: startsWith(github.ref, 'refs/tags/') == true && github.event.pull_request.head.repo.fork == false
uses: little-core-labs/[email protected]
id: releasetag
with:
tagRegex: "v?(.+)"

- name: docker - build release
if: startsWith(github.ref, 'refs/tags/') == true
uses: docker/build-push-action@v2
if: startsWith(github.ref, 'refs/tags/') == true && github.event.pull_request.head.repo.fork == false
uses: docker/build-push-action@v3
with:
context: .
file: ./docker/Dockerfile
Expand All @@ -150,13 +148,13 @@ jobs:
# docker build (branch)
- name: branch name
if: startsWith(github.ref, 'refs/tags/') == false
if: startsWith(github.ref, 'refs/tags/') == false && github.event.pull_request.head.repo.fork == false
id: branch-name
uses: tj-actions/branch-names@v2.2
uses: tj-actions/branch-names@v6.2

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

- name: docker - build branch
if: startsWith(github.ref, 'refs/tags/') == false
uses: docker/build-push-action@v2
if: startsWith(github.ref, 'refs/tags/') == false && github.event.pull_request.head.repo.fork == false
uses: docker/build-push-action@v3
with:
context: .
file: ./docker/Dockerfile
Expand All @@ -180,5 +178,6 @@ jobs:
# cleanup
- name: cleanup
if: github.event.pull_request.head.repo.fork == false
run: |
rm -f ${HOME}/.docker/config.json
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Autoscan

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

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

#### Simple example

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

Expand Down Expand Up @@ -117,8 +117,8 @@ Autoscan currently supports the following triggers:

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

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

All triggers support:

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

- Lidarr
- Radarr
- Readarr
- Sonarr

#### Connecting the -arrs

To add your webhook to Sonarr, Radarr or Lidarr, do:
To add your webhook to Sonarr, Radarr, Readarr or Lidarr, do:

1. Open the `settings` page in Sonarr/Radarr/Lidarr
1. Open the `settings` page in Sonarr/Radarr/Readarr/Lidarr
2. Select the tab `connect`
3. Click on the big plus sign
4. Select `webhook`
Expand Down Expand Up @@ -267,6 +268,10 @@ triggers:
- name: radarr4k # /triggers/radarr4k
priority: 5
readarr:
- name: readarr # /triggers/readarr
priority: 1
sonarr:
- name: sonarr-docker # /triggers/sonarr-docker
priority: 2
Expand Down Expand Up @@ -478,6 +483,10 @@ triggers:
- name: radarr4k # /triggers/radarr4k
priority: 5
readarr:
- name: readarr # /triggers/readarr
priority: 1
sonarr:
- name: sonarr-docker # /triggers/sonarr-docker
priority: 2
Expand Down
5 changes: 4 additions & 1 deletion cmd/autoscan/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/cloudbox/autoscan/triggers/lidarr"
"github.com/cloudbox/autoscan/triggers/manual"
"github.com/cloudbox/autoscan/triggers/radarr"
"github.com/cloudbox/autoscan/triggers/readarr"
"github.com/cloudbox/autoscan/triggers/sonarr"

// sqlite3 driver
Expand Down Expand Up @@ -59,6 +60,7 @@ type config struct {
Inotify []inotify.Config `yaml:"inotify"`
Lidarr []lidarr.Config `yaml:"lidarr"`
Radarr []radarr.Config `yaml:"radarr"`
Readarr []readarr.Config `yaml:"readarr"`
Sonarr []sonarr.Config `yaml:"sonarr"`
} `yaml:"triggers"`

Expand Down Expand Up @@ -268,8 +270,9 @@ func main() {
Int("bernard", len(c.Triggers.Bernard)).
Int("inotify", len(c.Triggers.Inotify)).
Int("lidarr", len(c.Triggers.Lidarr)).
Int("sonarr", len(c.Triggers.Sonarr)).
Int("radarr", len(c.Triggers.Radarr)).
Int("readarr", len(c.Triggers.Readarr)).
Int("sonarr", len(c.Triggers.Sonarr)).
Msg("Initialised triggers")

// targets
Expand Down
10 changes: 10 additions & 0 deletions cmd/autoscan/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/cloudbox/autoscan/triggers/lidarr"
"github.com/cloudbox/autoscan/triggers/manual"
"github.com/cloudbox/autoscan/triggers/radarr"
"github.com/cloudbox/autoscan/triggers/readarr"
"github.com/cloudbox/autoscan/triggers/sonarr"
)

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

for _, t := range c.Triggers.Readarr {
trigger, err := readarr.New(t)
if err != nil {
log.Fatal().Err(err).Str("trigger", t.Name).Msg("Failed initialising trigger")
}

r.Post(pattern(t.Name), trigger(proc.Add).ServeHTTP)
}

for _, t := range c.Triggers.Sonarr {
trigger, err := sonarr.New(t)
if err != nil {
Expand Down
114 changes: 114 additions & 0 deletions triggers/readarr/readarr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package readarr

import (
"encoding/json"
"net/http"
"path"
"strings"
"time"

"github.com/rs/zerolog/hlog"

"github.com/cloudbox/autoscan"
)

type Config struct {
Name string `yaml:"name"`
Priority int `yaml:"priority"`
Rewrite []autoscan.Rewrite `yaml:"rewrite"`
Verbosity string `yaml:"verbosity"`
}

// New creates an autoscan-compatible HTTP Trigger for Readarr webhooks.
func New(c Config) (autoscan.HTTPTrigger, error) {
rewriter, err := autoscan.NewRewriter(c.Rewrite)
if err != nil {
return nil, err
}

trigger := func(callback autoscan.ProcessorFunc) http.Handler {
return handler{
callback: callback,
priority: c.Priority,
rewrite: rewriter,
}
}

return trigger, nil
}

type handler struct {
priority int
rewrite autoscan.Rewriter
callback autoscan.ProcessorFunc
}

type readarrEvent struct {
Type string `json:"eventType"`
Upgrade bool `json:"isUpgrade"`

Files []struct {
Path string
} `json:"bookFiles"`
}

func (h handler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
var err error
l := hlog.FromRequest(r)

event := new(readarrEvent)
err = json.NewDecoder(r.Body).Decode(event)
if err != nil {
l.Error().Err(err).Msg("Failed decoding request")
rw.WriteHeader(http.StatusBadRequest)
return
}

l.Trace().Interface("event", event).Msg("Received JSON body")

if strings.EqualFold(event.Type, "Test") {
l.Info().Msg("Received test event")
rw.WriteHeader(http.StatusOK)
return
}

//Only handle test and download. Everything else is ignored.
if !strings.EqualFold(event.Type, "Download") || len(event.Files) == 0 {
l.Error().Msg("Required fields are missing")
rw.WriteHeader(http.StatusBadRequest)
return
}

unique := make(map[string]bool)
scans := make([]autoscan.Scan, 0)

for _, f := range event.Files {
folderPath := path.Dir(h.rewrite(f.Path))
if _, ok := unique[folderPath]; ok {
continue
}

// add scan
unique[folderPath] = true
scans = append(scans, autoscan.Scan{
Folder: folderPath,
Priority: h.priority,
Time: now(),
})
}

err = h.callback(scans...)
if err != nil {
l.Error().Err(err).Msg("Processor could not process scans")
rw.WriteHeader(http.StatusInternalServerError)
return
}

rw.WriteHeader(http.StatusOK)
l.Info().
Str("path", scans[0].Folder).
Str("event", event.Type).
Msg("Scan moved to processor")
}

var now = time.Now
Loading

0 comments on commit ff17077

Please sign in to comment.