Skip to content

phrontizo/DebridMovieMapper

Repository files navigation

DebridMovieMapper

A Rust-based service that maps your Real-Debrid torrent library to a Jellyfin/Plex-compatible WebDAV endpoint with automatic media identification via TMDB.

I created this project as:

  • I was using the various arrs, and found it cumbersome, plus I ran out of storage space, meaning I needed to use a Debrid service of some kind
  • Zurg didn't work for me as I needed the folder structure for Jellyfin

I use Debrid Media Manager for keeping Real Debrid populated at the moment, but am thinking of incorporating that into this service as well.

Note that this is still a work in progress and is provided as-is for educational purposes only.

Future work:

  • Test with Kodi
  • Add a web-ui to track progress and correct mismatches
  • Automatically populate Real-Debrid with watchlist content from Trakt and newly released episodes from already tracked shows

This was 100% vibe coded using a mix of Claude and Junie as further AI experimentation.

Features

  • Media Identification: Automatically identifies movies and TV shows using TMDB metadata based on torrent filenames.
  • Jellyfin/Plex Structure: Organizes your library into a clean Movies/ and Shows/ directory structure.
  • Season Grouping: Automatically groups TV show episodes into Season XX folders.
  • WebDAV Endpoint: Exposes a WebDAV server (port 8080) serving proxied media files with real file sizes and extensions. Media bytes are fetched on demand from Real-Debrid's CDN. Mount via rclone for use with Jellyfin/Plex.
  • On-Demand Repair: Detects broken links at playback time (503 from Real-Debrid) and attempts instant synchronous repair. For cached torrents, playback continues after a ~1-2s delay; for non-cached torrents, a new download is started automatically.
  • Persistent Cache: Uses an embedded database (redb) to cache media identifications, reducing API calls and speeding up restarts.
  • Configurable Scan Interval: Customizable scan interval via environment variable.
  • Robust Identification Logic: Handles complex torrent naming conventions, including CamelCase splitting, technical metadata stripping, and multi-service fallback strategies.
  • Jellyfin Notifications: Optionally notifies Jellyfin when content changes so new episodes and movies appear immediately without waiting for a full library scan.

Prerequisites

  • A Real-Debrid account and API Token.
  • A TMDB API Key (The Movie Database).

Configuration

The service is configured via environment variables. You can use a .env file in the project root:

RD_API_TOKEN=your_real_debrid_token
TMDB_API_KEY=your_tmdb_api_key

# Optional
SCAN_INTERVAL_SECS=60       # How often to scan for new torrents (default: 60, minimum: 10)
DB_PATH=metadata.db         # Path to the redb database file (default: metadata.db)
PORT=8080                   # WebDAV server listen port (default: 8080)

# Optional: Jellyfin integration (all three required to enable)
JELLYFIN_URL=http://jellyfin:8096
JELLYFIN_API_KEY=your_jellyfin_api_key
JELLYFIN_RCLONE_MOUNT_PATH=/media

Environment Variables

Variable Required Default Description
RD_API_TOKEN Yes - Your Real-Debrid API token
TMDB_API_KEY Yes - Your TMDB (The Movie Database) API key
SCAN_INTERVAL_SECS No 60 Interval between torrent library scans in seconds (minimum: 10, runs immediately on startup)
DB_PATH No metadata.db Path to the redb database file
PORT No 8080 WebDAV server listen port
JELLYFIN_URL No - Jellyfin server URL for library update notifications
JELLYFIN_API_KEY No - Jellyfin API key for authentication
JELLYFIN_RCLONE_MOUNT_PATH No - rclone mount path as seen by Jellyfin (e.g. /media)

Running with Docker

The easiest way to run DebridMovieMapper is using Docker. Pre-built multi-platform images are available via the GitHub Container Registry.

1. Pull the image

Images are available from both GitHub Container Registry and Docker Hub:

# GitHub Container Registry
docker pull ghcr.io/phrontizo/debridmoviemapper:latest

# Docker Hub
docker pull phrontizo/debridmoviemapper:latest

Available tags:

  • :latest — latest stable release (updated on version tags like v1.0.2)
  • :edge — latest build from main branch (may be unstable)
  • :1.0.2, :1.0, :1 — pinned to a specific release version

2. Run the container

docker run -d \
  --name debridmoviemapper \
  -p 8080:8080 \
  -e RD_API_TOKEN=your_token \
  -e TMDB_API_KEY=your_api_key \
  -e SCAN_INTERVAL_SECS=60 \
  -v debridmoviemapper-metadata:/data \
  ghcr.io/phrontizo/debridmoviemapper:latest

Building from source (Optional)

Build locally for your current architecture:

docker build -t debridmoviemapper .

Multi-platform images are built and pushed automatically by GitHub Actions. Release tags (e.g. git tag v1.0.2 && git push origin v1.0.2) update :latest and semver tags; pushes to main update :edge.

Note: The named volume ensures your media identification cache is preserved across container recreations.

Upgrading from a bind-mounted metadata.db: If you previously used -v $(pwd)/metadata.db:/metadata.db, switch to a named volume. You can let the database regenerate automatically (TMDB identifications will be re-fetched on the first scan), or copy your existing file into the volume.

Upgrading from sled (pre-redb): If you previously ran an older version that used sled, your metadata.db will be a directory. Remove it before starting the new version: rm -rf metadata.db. The redb database will be recreated automatically.

Docker Compose Setup with Jellyfin

The recommended way to use DebridMovieMapper with Jellyfin is via Docker Compose with rclone mounting the WebDAV endpoint.

A complete compose.yml file is provided in the repository that includes:

  • DebridMovieMapper service
  • rclone for mounting the WebDAV endpoint
  • Jellyfin for media playback

Setup Steps

  1. Create the rclone mount directory:

    mkdir -p rclone && chown 65534:65534 rclone
  2. Create a .env file with your credentials (or export them in your environment):

    RD_API_TOKEN=your_real_debrid_token
    TMDB_API_KEY=your_tmdb_api_key
    JELLYFIN_API_KEY=your_jellyfin_api_key  # optional
  3. Start the services:

    docker compose up -d
  4. Access Jellyfin at http://localhost:8096 and add /media as a library path

  5. (Optional) To enable instant library updates, set JELLYFIN_API_KEY in your .env file

Notes

  • The WebDAV server runs on port 8080 (mapped to 8080 on the host)
  • A Docker healthcheck ensures rclone only starts after the WebDAV server is ready (prevents empty mount on startup)
  • Media identifications are persisted in a named Docker volume (metadata)
  • rclone mounts the WebDAV endpoint to ./rclone on the host via a bind mount with rshared propagation (required for FUSE mounts to be visible to other containers)
  • Jellyfin reads from /media which is bind-mounted from ./rclone
  • Files appear as real media files (.mkv, .mp4, etc.) with correct file sizes — media bytes are proxied from Real-Debrid's CDN on demand
  • Jellyfin probes and plays content normally as if the files were local

Usage

Once running, the WebDAV server will be available at http://localhost:8080. Mount it with rclone and point your media server at the mount:

  • Jellyfin (via rclone mount - see Docker Compose example above)
  • Plex (via rclone mount)
  • Kodi
  • Infuse (iOS/tvOS/macOS)

Technical Details

  • Language: Rust (2021 edition)
  • Libraries:
    • dav-server: WebDAV protocol implementation.
    • tokio: Async runtime.
    • reqwest: HTTP client with rustls.
    • redb: Embedded ACID key-value store.
    • serde: Serialization/Deserialization.
    • regex: Filename parsing and cleaning.

Project Structure

  • src/main.rs: Entry point — initialises shared state and starts the WebDAV server.
  • src/tasks.rs: Background scan loop — polls Real-Debrid, identifies new torrents, updates the VFS.
  • src/rd_client.rs: Real-Debrid API client with adaptive token bucket rate limiter and response caching.
  • src/tmdb_client.rs: TMDB API client for media metadata.
  • src/repair.rs: Torrent repair state machine with synchronous instant repair for cached torrents.
  • src/vfs.rs: Virtual File System logic for library organisation.
  • src/dav_fs.rs: WebDAV filesystem — re-unrestricts links on read, attempts instant repair on 503.
  • src/identification.rs: Smart media identification and filename cleaning logic.
  • src/error.rs: Unified error type (AppError) using thiserror.
  • src/jellyfin_client.rs: Optional Jellyfin notification client for instant library updates.
  • src/mapper.rs: Library root (module declarations).

How It Works

Background Tasks

The service runs one background task:

  1. Scan Task: Polls Real-Debrid for new/updated torrents and updates the virtual filesystem
    • Runs immediately on startup
    • Repeats every SCAN_INTERVAL_SECS (default: 60 seconds)
    • Builds the VFS with restricted RD links; unrestriction happens lazily at read time

On-Demand Repair

There is no background repair loop. Instead, repair is triggered synchronously at playback time:

  • When a media file is read, dav_fs re-calls unrestrict_link for a fresh URL (the 1-hour response cache makes this free when the torrent is healthy)
  • If unrestrict_link returns a 503, try_instant_repair is called synchronously: re-adds the torrent via magnet, selects the same files, and checks if the torrent is already cached on Real-Debrid's servers
  • Cached torrents (most common): repair completes in ~1-2 seconds and the new link is unrestricted inline — playback continues after a brief delay
  • Non-cached torrents: the file returns an error, the old torrent is deleted, and the new torrent is left to download (the scan loop picks it up automatically)
  • Non-cached/repairing torrents are hidden from WebDAV until healthy again

Jellyfin Notifications

When JELLYFIN_URL, JELLYFIN_API_KEY, and JELLYFIN_RCLONE_MOUNT_PATH are all set, the service notifies Jellyfin of specific changed paths after each VFS update. This uses Jellyfin's POST /Library/Media/Updated API to trigger targeted scans of only the affected folders (e.g. a single season directory for a new episode), avoiding full library rescans. Changes from all sources — new torrents, deletions, repairs — are detected automatically.

Archive-Only Torrents

Some torrents contain RAR/ZIP archives instead of video files. Real-Debrid does not extract these archives, so they cannot be streamed. When such a torrent is detected, a warning is logged:

WARN Torrent 'Movie.Name.1080p.BluRay' contains only archive files (RAR/ZIP) and cannot be streamed — replace with a non-archive version on Real-Debrid

To fix this, delete the torrent from Real-Debrid and find an alternative release that contains video files directly (.mkv, .mp4, etc.).

Error Handling

  • 503 Service Unavailable: Triggers synchronous instant repair — succeeds inline for cached torrents, fails for non-cached
  • 429 Rate Limit: Adaptive token bucket rate limiter shared across all API calls — on 429, the global request interval doubles (max 2s between requests) and Retry-After headers are respected; on success, the interval gradually recovers toward the baseline of 10 req/s
  • 404 Not Found: Treated as success for delete operations (idempotent)
  • Playback Errors: WebDAV read failures on 503 trigger instant repair (rate-limited to 30s cooldown, max 3 attempts per torrent)

Caching

  • Unrestrict responses: Cached for 1 hour to reduce API load
  • TMDB metadata: Persisted to embedded database (metadata.db) indefinitely

Licence

MIT

About

A Rust-based service that maps your Real-Debrid torrent library to a Jellyfin/Plex-compatible WebDAV endpoint with automatic media identification via TMDB.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors