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.
- 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/andShows/directory structure. - Season Grouping: Automatically groups TV show episodes into
Season XXfolders. - 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.
- A Real-Debrid account and API Token.
- A TMDB API Key (The Movie Database).
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| 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) |
The easiest way to run DebridMovieMapper is using Docker. Pre-built multi-platform images are available via the GitHub Container Registry.
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:latestAvailable tags:
:latest— latest stable release (updated on version tags likev1.0.2):edge— latest build frommainbranch (may be unstable):1.0.2,:1.0,:1— pinned to a specific release version
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:latestBuild 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.
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
-
Create the rclone mount directory:
mkdir -p rclone && chown 65534:65534 rclone -
Create a
.envfile 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 -
Start the services:
docker compose up -d
-
Access Jellyfin at
http://localhost:8096and add/mediaas a library path -
(Optional) To enable instant library updates, set
JELLYFIN_API_KEYin your.envfile
- 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
./rcloneon the host via a bind mount withrsharedpropagation (required for FUSE mounts to be visible to other containers) - Jellyfin reads from
/mediawhich 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
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)
- Language: Rust (2021 edition)
- Libraries:
dav-server: WebDAV protocol implementation.tokio: Async runtime.reqwest: HTTP client withrustls.redb: Embedded ACID key-value store.serde: Serialization/Deserialization.regex: Filename parsing and cleaning.
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) usingthiserror.src/jellyfin_client.rs: Optional Jellyfin notification client for instant library updates.src/mapper.rs: Library root (module declarations).
The service runs one background task:
- 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
There is no background repair loop. Instead, repair is triggered synchronously at playback time:
- When a media file is read,
dav_fsre-callsunrestrict_linkfor a fresh URL (the 1-hour response cache makes this free when the torrent is healthy) - If
unrestrict_linkreturns a 503,try_instant_repairis 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
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.
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.).
- 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)
- Unrestrict responses: Cached for 1 hour to reduce API load
- TMDB metadata: Persisted to embedded database (
metadata.db) indefinitely
MIT