autopulse

repository·main·Indexed 19 days ago

https://github.com/dan-online/autopulse

A media automation tool that bridges media managers like Sonarr, Radarr, Lidarr, and Readarr with media servers such as Plex, Jellyfin, Emby, and Audiobookshelf. It uses push-based webhooks to trigger targeted library refreshes instead of full-library scans, supporting path rewrites, file verification, and multiple database backends including SQLite and Postgres.

Tokens
22.8K
Snippets
89
Records
107
Agent score
68%

What's inside autopulse

  1. Use path rewrites for triggers and targets

    main

    Use the rewrite configuration to map paths sent by a trigger to the paths autopulse or your media targets actually use.

    Trigger Rewrites

    Trigger rewrites run before the event is stored. Use these when the path in the webhook payload (from Sonarr, Radarr, etc.) does not match the path autopulse should process.

    • rewrite.from: A regex pattern representing the path in the webhook payload. It is recommended to anchor it (e.g., ^/downloads) to ensure precision.
    • rewrite.to: The path that autopulse should use for subsequent processing.

    Target Rewrites

    Target rewrites run later per target. These are useful when different media servers (Plex, Jellyfin, Emby) see the same library through different mount paths.

    Note: If the source app and the target already use the same path, you can omit the rewrite section.

  2. Configure filesystem access and path checking

    main

    Autopulse can operate without having your media files mounted inside its container, but behavior changes based on the opts.check_path setting:

    • opts.check_path = false (Default): Autopulse can route webhook paths without reading the files themselves. This is ideal for minimal container footprints.
    • opts.check_path = true: The rewritten path must exist inside the autopulse runtime/container before the event is sent to targets. This is required if you use hash checks or anchors, as these operations require filesystem access.

    Best Practice: If you enable path checking, mount your media paths as read-only inside the container and ensure your rewrite rules point to the path as seen from inside the autopulse container.

  3. How autopulse works: Triggers and Targets

    main

    autopulse is a web server that automates media server updates by acting as a bridge between media organizers and media servers.

    Core Concepts

    • Trigger: An endpoint specification that receives notifications when a file is ready. Examples include:

      • Media Organizers: Sonarr, Radarr, Lidarr, Readarr.
      • Manual: A default endpoint (/triggers/manual) for manual testing or custom workflows.
      • Notify: Uses OS-level file system events (inotify on Linux, FSEvents on MacOS, ReadDirectoryChangesW on Windows) or polling as a fallback.
      • Fileflows: Via sub-flows.
    • Target: A specification for a library or service that needs updating once a file is processed. Examples include:

      • Media Servers: Plex, Jellyfin, Emby, Audiobookshelf.
      • Automation/Commands: Tdarr, FileFlows, or a custom command (executing a shell command).
      • Integration: Another autopulse instance or Sonarr/Radarr.

    Example Workflow

    1. Trigger: Sonarr organizes an episode and sends a webhook to autopulse.
    2. Processing: autopulse rewrites the file path to match what the target expects.
    3. Verification: autopulse optionally verifies the file exists and/or waits for a specific file hash.
    4. Target: autopulse sends a request to Plex to update the metadata/information.
  4. Configure webhook connections for Arr apps

    main

    To integrate Sonarr, Radarr, Lidarr, or Readarr with autopulse, create a webhook or connection in the source application that sends a POST request to the following URL format:

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

    Configuration Details:

    • Trigger Name: This corresponds to the name defined in your autopulse configuration (e.g., if your config has [triggers.radarr], the URL is /triggers/radarr).
    • Authentication: Use the credentials configured under auth.username and auth.password in your autopulse config.
    • Required Events: Enable events that include file paths, such as import/download, upgrade, rename, and delete.
    http://<autopulse-host>:2875/triggers/<trigger-name>
  5. Install autopulse on Unraid

    main

    autopulse can be installed via the Unraid Community Apps template.

    1. Manual Installation (if not yet in Community Apps): SSH into your Unraid server and run:
      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
    2. WebUI Installation:
      • Open the Apps tab in Unraid.
      • Select Private apps from the left sidebar.
      • Click Install on autopulse.
    3. Configuration:
      • Adjust the Media share path.
      • CRITICAL: Change AUTOPULSE__AUTH__PASSWORD from the default change-me before applying settings.
  6. Set up autopulse for development

    main

    To run autopulse from source, ensure you have Rust installed.

    Basic Setup

    # clone the repo
    $ git clone https://github.com/dan-online/autopulse.git
    $ cd autopulse
    
    # create a basic config
    $ cat <<EOF > config.toml
    [app]
    database_url = "sqlite://data/test.sqlite"
    log_level = "trace"
    EOF
    
    # Run using vendored/bundled dependencies
    $ cargo run --features vendored

    Running with specific dependencies

    If you have system dependencies installed (like libql-dev or libsqlite3-dev), you can run without the vendored feature:

    # Standard run
    $ cargo run
    
    # Run with only SQLite support
    $ cargo run --no-default-features --features sqlite
    
    # Run with only Postgres support
    $ cargo run --no-default-features --features postgres
    cargo run --features vendored
  7. Install autopulse via Docker

    main

    The easiest way to run autopulse is using Docker. You can pull images from ghcr.io/dan-online/autopulse or Docker Hub.

    Available Tags

    • latest: Full image with support for both Postgres and SQLite.
    • latest-postgres: Smaller image supporting only Postgres.
    • latest-sqlite: Smaller image supporting only SQLite.
    • stable: The latest versioned release.

    Note: All images support linux/amd64 and linux/arm64. You can use suffixes like -amd64 or -arm64 to specify architecture.

    # Example: Running with SQLite
    docker run -d --net autopulse -e AUTOPULSE__APP__DATABASE_URL=sqlite://database.db --name autopulse ghcr.io/dan-online/autopulse
    
    # Example: Running with Postgres
    docker run -d --net autopulse -e AUTOPULSE__APP__DATABASE_URL=postgres://postgres:autopulse@postgresql/autopulse --name autopulse ghcr.io/dan-online/autopulse
  8. Choose the correct Docker tag or database type

    main

    Docker Tags

    • stable: The latest versioned release (recommended for production).
    • latest: The newest Docker build from the main branch.

    Database Selection

    • Default Image: Supports both SQLite and Postgres.
    • -sqlite tag: Optimized for SQLite only. Best for most single-instance home installs.
    • -postgres tag: Optimized for Postgres only. Best for high concurrency or existing Postgres environments.

    Database Connection Strings:

    • For SQLite: sqlite://path/to/data.sqlite
    • For In-Memory (testing/ephemeral): sqlite://:memory:
  9. How Notify trigger event filtering works

    main

    The Notify trigger processes file system events through several stages of filtering and transformation:

    1. Regex Filtering: If filters are provided, the raw file path must match at least one of the regex strings.
    2. Path Rewriting: If a rewrite configuration is present, the path is transformed using the provided Rewrite rules.
    3. PathFilter: The (potentially rewritten) path is then checked against the filter (PathFilter) to see if it is allowed.
    4. Event Kind Filtering: Only specific file system events are emitted to the system:
      • Access(AccessKind::Close(AccessMode::Write))
      • Modify(ModifyKind::Metadata(_))
      • Modify(ModifyKind::Name(RenameMode::Both))
      • Create(_)
      • Remove(_)
  10. Handle client IP detection and trusted proxies

    main

    The authentication system determines the client's IP address for rate limiting. It uses the following logic:

    1. It first checks the connection's peer_addr.
    2. If the peer_addr is in the manager.settings.app.trusted_proxies list, it inspects the X-Forwarded-For header.
    3. It parses the X-Forwarded-For list from right to left, selecting the first IP address that is not in the trusted proxies list.
    4. If no untrusted IP is found in the header, or if the header is missing/malformed, it falls back to the peer_addr.
  11. Base Path Normalization rules

    main

    The base_path field in the App configuration is automatically normalized during deserialization to ensure UI routes are well-formed. The normalization logic follows these rules:

    1. Leading Slash: If a leading slash is missing, one is added (e.g., autopulse becomes /autopulse).
    2. Trailing Slash: Any trailing slashes are stripped (e.g., /autopulse/ becomes /autopulse).
    3. Empty/Root Path: A lone slash / or an empty string "" is collapsed to an empty string "".
    4. Whitespace: Leading and trailing whitespace is trimmed.

    Note for Reverse Proxies: The UI routes are mounted under this prefix server-side. Your reverse proxy must pass this prefix through verbatim to the service without stripping it.

    // Examples of base_path normalization:
    { "base_path": "" }           // -> ""
    { "base_path": "/" }          // -> ""
    { "base_path": "autopulse" }  // -> "/autopulse"
    { "base_path": "/autopulse/" }// -> "/autopulse"
    { "base_path": "  /app// " }  // -> "/app"
  12. Configure Discord mention targets and triggers

    main

    A DiscordMention defines who gets mentioned and when.

    Targets (targets): Each entry in the targets list can be one of the following:

    • A special mention literal: "here" or "everyone".
    • A role mention: A single-key map {"role": "ROLE_ID"}.
    • A user mention: A single-key map {"user": "USER_ID"}.

    Triggers (on): The on field is a list of EventType values.

    • If on is provided, the mention only fires when an event in the batch matches one of these types.
    • If on is empty or omitted, the mention fires for every event in the batch.

    Validation Error: If you provide a targets list that is empty, the configuration will fail with the error: discord mention: targets must be non-empty (entries: "here", "everyone", or a single-key map keyed by "role" or "user").

    # Mention a specific role only when a job fails
    - targets: [{ role: "111222333444" }]
      on: [failed]
    
    # Mention @here for every event
    - targets: [here]