MCPcopy Index your code
hub / github.com/dan-online/autopulse

github.com/dan-online/autopulse @v2.0.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v2.0.0 ↗ · + Follow
876 symbols 2,171 edges 97 files 40 documented · 5%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

[![Forks][forks-shield]][forks-url] [![Stargazers][stars-shield]][stars-url] [![Issues][issues-shield]][issues-url]

Logo

autopulse

💫 automated lightweight service that updates media servers like Plex and Jellyfin

based on notifications from media organizers like Sonarr and Radarr

<a href="https://github.com/dan-online/autopulse/issues">Report Bug</a>
·
<a href="https://github.com/dan-online/autopulse/issues">Request Feature</a>
.
<a href="https://autopulse.dancodes.online/">Documentation</a>
.
<a href="https://autopulseui.pages.dev/">autopulseUI</a>

About The Project

autopulse is a web server that receives notifications from media organizers like Sonarr/Radarr/Lidarr/etc (triggers) and updates the items in media servers like Plex/Jellyfin/Emby/etc (targets). It is designed to be efficient, only updating the items that have changed, reducing the load on media servers.

Terminology

We use the following terminology: - Trigger: A trigger is a specification for an endpoint that will be called when a file is ready to be processed - Manual (default: /triggers/manual) - Fileflows (sub-flow) - Sonarr - Radarr - Lidarr - Readarr - Notify - Linux: inotify - MacOS: FSEvents - Windows: ReadDirectoryChangesW - Fallback: polling - Target: A target is a specification for a library that will be updated when a file is ready to be processed - Plex - Jellyfin - Emby - Command - Sonarr - Radarr - Tdarr - FileFlows - Audiobookshelf - Another autopulse instance

Example Flow

  1. Sonarr organizes an episode and sends a webhook notification to autopulse
  2. autopulse receives the notification and rewrites the path to the expected path for the target
  3. autopulse optionally checks the file exists and optionally waits for the file to match a provided hash
  4. autopulse sends a request to Plex to update or add the episode information/metadata

Features

  • Rewrites: rewrites the path provided by the trigger to the path expected by the target
  • Integration: integrates with Sonarr, Radarr, Plex, Jellyfin, and more in the future
  • Checks: checks the file exists before updating the target and optionally waits for the file to match a provided hash
  • Reliability: uses a database to store the state of the scan requests
  • Webhooks: allow for notifications to be sent when a file is ready to be processed with Discord, Matrix Hookshot, or generic JSON webhooks
  • User-Interface: provides a simple web interface to view/add scan requests

Getting Started

Docker

The easiest way to get started with autopulse is to use the provided docker image on ghcr.io or Docker Hub

Tags

  • latest - full image with support for postgres/sqlite
  • latest-postgres - smaller image that only supports Postgres
  • latest-sqlite - smaller image that only supports SQLite
  • stable - latest versioned release

All images are multi-arch and support linux/amd64, linux/arm64, however -amd64 and -arm64 suffixes can be used to specify the architecture

Unraid

An Unraid Community Apps template lives in unraid/autopulse.xml.

Once autopulse is listed in Community Apps, open the Apps tab in Unraid, search for autopulse, and click Install. Until then, install it as a private Community App:

  1. Make sure Community Applications is installed (it ships with Unraid via the Apps tab).
  2. SSH to your Unraid server and run:

bash mkdir -p /boot/config/plugins/community.applications/private/autopulse wget -O /boot/config/plugins/community.applications/private/autopulse/autopulse.xml \ https://raw.githubusercontent.com/dan-online/autopulse/main/unraid/autopulse.xml

  1. In the WebUI, open Apps and select Private apps from the left sidebar, then click Install on autopulse.
  2. Adjust the Media share path and change AUTOPULSE__AUTH__PASSWORD from the default change-me before clicking Apply.

Compose

Docker Compose files for both SQLite and Postgres are provided in the example

CLI

# create a network
$ docker network create autopulse

# postgres database
$ docker run -d --net autopulse --name postgres -e POSTGRES_PASSWORD=autopulse -e POSTGRES_DB=autopulse postgres
$ docker run -d --net autopulse -e AUTOPULSE__APP__DATABASE_URL=postgres://postgres:autopulse@postgresql/autopulse --name autopulse ghcr.io/dan-online/autopulse

# sqlite database
$ docker run -d --net autopulse -e AUTOPULSE__APP__DATABASE_URL=sqlite://database.db --name autopulse ghcr.io/dan-online/autopulse
# or in-memory
$ docker run -d --net autopulse -e AUTOPULSE__APP__DATABASE_URL=sqlite://:memory: --name autopulse ghcr.io/dan-online/autopulse

Documentation

All the documentation can be found on the website

Here's some quick links:

Quick Start

Configuration

autopulse requires a configuration file to run. By default, it searches the current working directory for config.toml, config.yaml, config.yml, or config.json in that order. You can pass --config /path/to/config.toml to load an explicit file, and override values by setting environment variables in the format of: AUTOPULSE__{SECTION}__{KEY}.

For example: AUTOPULSE__APP__DATABASE_URL

An example has been provided in the example directory

Note: You can provide the config as config.toml, config.yaml, config.yml, or config.json

Note: You can also provide the path to a variable by appending __FILE For example: AUTOPULSE__AUTH__PASSWORD__FILE=/run/secrets/autopulse_password

Authorization

autopulse uses basic authorization for the API. You can set the username and password in the config file or by setting the AUTOPULSE__AUTH__USERNAME and AUTOPULSE__AUTH__PASSWORD environment variables.

auth:
  username: terry
  password: yoghurt

Note: By default the username and password are admin and password respectively, however it is HIGHLY recommended to change these values if you are exposing the API to the internet.

Examples

triggers:
  my_sonarr:
    type: "sonarr"
    rewrite:
      from: "/downloads"
      to: "/tvshows"
    filter:
      exclude:
        - "^/tvshows/extras/"

  my_radarr:
    type: "radarr"
    rewrite:
      from: "/downloads"
      to: "/movies"

  my_manual:
    type: "manual"
    rewrite:
      from: "/downloads"
      to: "/"

  my_notify:
    type: "notify"
    paths:
      - "/watch"
    rewrite:
      from: "/watch"
      to: "/media"

webhooks:
  my_discord:
    type: "discord"
    url: "https://discord.com/api/webhooks/1234567890/abcdefg"

  my_discord_with_mentions:
    type: "discord"
    url: "https://discord.com/api/webhooks/1234567890/abcdefg"
    mentions:
      - targets:
          - here
          - role: "1234567890"
          - user: "9876543210"
        on: [failed, hash_mismatch]
      - targets: [everyone]

  my_hookshot:
    type: "hookshot"
    url: "https://matrix.example.com/_matrix/hookshot/webhook/abcdefg"

  my_json:
    type: "json"
    url: "https://example.com/webhooks/autopulse"

targets:
  my_plex:
    type: "plex"
    url: "http://plex:32400"
    token: "<your_token>"

  my_different_plex:
    type: "plex"
    url: "http://plex:32401"
    token: "<your_token>"
    rewrite:
      from: "/media"
      to: "/plex"

  my_jellyfin:
    type: "jellyfin"
    url: "http://jellyfin:8096"
    token: "<your_token>"

  my_audiobookshelf:
    type: "audiobookshelf"
    url: "http://audiobookshelf:13378"
    token: "<your_token>"
    filter:
      include:
        - "^/audiobooks/"
      exclude:
        - "/samples/"

  my_command:
    type: "command"
    raw: "echo $FILE_PATH >> list.txt"

Manual

By default a manual endpoint is provided which can be used to manually trigger a scan. This can be useful for testing or for when you want to trigger a scan without waiting for a file to be ready.

$ curl -u 'admin:password' 'http://localhost:2875/triggers/manual?path=/path/to/file&hash=1234567890' 
# or
$ curl -H 'Authorization: Basic <base_64_encoded_login>' 'http://localhost:2875/triggers/manual?path=/path/to/file&hash=1234567890'

Configuration Template API

autopulse provides a configuration template API that allows external applications to dynamically generate configurations without embedding static TOML files. This is useful for applications like Bazarr that need to configure autopulse programmatically.

GET /api/config-template

Returns configuration templates with optional parameters:

# Get basic templates
$ curl -u "admin:password" "http://localhost:2875/api/config-template"

# Get templates with specific types
$ curl -u "admin:password" "http://localhost:2875/api/config-template?database=postgres&triggers=sonarr,radarr&targets=plex,jellyfin&output=json"

Query Parameters: - database: Database type (sqlite, postgres) - triggers: Comma-separated trigger types (manual, sonarr, radarr, etc) - targets: Comma-separated target types (plex, jellyfin, emby, etc) - output: Output format (json, toml)

UI

The web UI ships in the main autopulse image and is served at /ui/* on the same port (default 2875). It lets you browse scan events, retry failures, view config, and submit manual scans.

Default credentials are admin / password (the same as the API auth). Change them via the standard auth.username / auth.password config keys; sessions issued under old credentials are invalidated automatically.

Reverse Proxy

To serve the UI behind a reverse proxy with a path prefix, set app.base_path and have the proxy pass the prefix through (no strip-prefix). UI routes mount under base_path server-side. See app settings for the full list of relevant options.

To-do

  • [x] Add more triggers
  • [x] Lidarr
  • [x] Readarr
  • [x] inotify
  • [x] Move triggers to structs
  • [ ] Hooks
  • [ ] Add/Found/Processed hooks
  • [ ] Move Webhooks to hook
  • [ ] Command hook
  • [x] Add more targets
  • [x] Emby
  • [ ] Add more webhooks
  • [ ] Generic JSON
  • [x] Add more options
  • [x] Cleanup duration (currently 10 days)
  • [x] Jellyfin metadataRefreshMode currently set to FullRefresh
  • [x] Plex refresh
  • [x] Databases
  • [x] SQLite
  • [-] MySQL - linking mysql for alpine docker image is quite complex, so for now not supported unless someone can figure it out
  • [x] UI
  • [x] Add/View scan requests
  • [ ] Add/View triggers
  • [ ] Add/View targets
  • [ ] Add/View webhooks

Contributing

Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.

This project follows the Conventional Commits specification

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feat/AmazingFeature)
  3. Commit your Changes (git commit -m 'feat: add some AmazingFeature')
  4. Push to the Branch (git push origin feat/AmazingFeature)
  5. Open a Pull Request

Development

If you're a nix user then you can use the provided flake.nix to get started

Dependencies

Setup

# clone the repo
$ git clone https://github.com/dan-online/autopulse.git
$ cd autopulse

# basic easy config
$ cat <<EOF > config.toml
[app]
database_url = "sqlite://data/test.sqlite"
log_level = "trace"
EOF

# easy start using vendored/bundled dependencies
$ cargo run --features vendored

# or if you have the dependencies installed (libql-dev, libsqlite3-dev)
$ cargo run
# or if you only have one of the dependencies installed
$ cargo run --no-default-features --features sqlite   # for sqlite
$ cargo run --no-default-features --features postgres # for postgres

FAQ

What URL do I put in Sonarr, Radarr, Lidarr, or Readarr?

Create a webhook/connection in the source app that points at the matching autopulse trigger:

http://<autopulse-host>:2875/triggers/<trigger-name>

For example, a config entry named triggers.radarr uses /triggers/radarr; triggers.my_radarr uses /triggers/my_radarr. Use POST and the same basic auth credentials configured under auth.username and auth.password.

Enable events that include file paths, such as import/download, upgrade, rename, and delete.

Which paths should I use for rewrite.from and rewrite.to?

Use rewrite only when the path sent by the trigger is not the path autopulse or a target should use.

  • from is the path pattern in the webhook payload from Sonarr, Radarr, Lidarr, Readarr, etc.
  • to is the path autopulse should process next.

For Arr triggers, this usually means the final media path from the Arr app, not the temporar

Extension points exported contracts — how you extend this code

TriggerConfig (Interface)
(no doc) [7 implementers]
crates/service/src/settings/triggers/mod.rs
TargetProcess (Interface)
(no doc) [10 implementers]
crates/service/src/settings/targets/mod.rs
TriggerRequest (Interface)
(no doc) [4 implementers]
crates/service/src/settings/triggers/mod.rs
WebhookHttpClient (Interface)
(no doc) [2 implementers]
crates/service/src/settings/webhooks/transport.rs
WebhookSleeper (Interface)
(no doc) [2 implementers]
crates/service/src/settings/webhooks/transport.rs

Core symbols most depended-on inside this repo

is_empty
called by 53
crates/service/src/settings/path_filter.rs
filter
called by 43
crates/service/src/settings/targets/mod.rs
get
called by 35
crates/service/src/settings/timer.rs
get_conn
called by 29
crates/database/src/conn.rs
get_url
called by 26
crates/utils/src/get_url.rs
perform
called by 24
crates/service/src/settings/targets/mod.rs
add_event
called by 19
crates/service/src/settings/webhooks/manager.rs
get_path
called by 16
crates/database/src/models.rs

Shape

Function 506
Method 208
Class 123
Enum 32
Interface 7

Languages

Rust78%
TypeScript22%

Modules by API surface

crates/server/static/htmx.min.js188 symbols
crates/service/src/settings/webhooks/discord.rs44 symbols
crates/service/src/settings/webhooks/transport.rs33 symbols
crates/service/src/settings/targets/plex.rs26 symbols
crates/service/src/settings/targets/emby.rs26 symbols
crates/server/src/ui/auth.rs25 symbols
crates/database/src/conn.rs24 symbols
crates/service/src/settings/mod.rs23 symbols
crates/service/src/manager.rs16 symbols
crates/service/src/settings/targets/fileflows.rs13 symbols
crates/service/src/settings/path_filter.rs13 symbols
crates/server/src/routes/config.rs13 symbols

Datastores touched

autopulseDatabase · 1 repos
dbDatabase · 1 repos

For agents

$ claude mcp add autopulse \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact