How It Works
Event flow
┌──────────┐ webhook ┌──────────────┐ ffprobe ┌─────────┐
│ Radarr/ │ ──────────────►│ │ ─────────────►│ media │
│ Sonarr │ POST │ multilinguarr│ │ file │
└──────────┘ /webhook/ │ │◄──────────────│ │
radarr-fr │ │ audio tracks │ │
│ │ └─────────┘
│ │
│ ┌──────────┤
│ │ SQLite │ job queue +
│ │ database │ config store
│ └──────────┤
│ │
│ │── symlink ──► library/movies/fr/
│ │── symlink ──► library/movies/en/
└──────────────┘Step by step
- Webhook received — Radarr or Sonarr sends a POST to
/webhook/{instance}when a file is downloaded, upgraded, or deleted - Job enqueued — the webhook payload is stored in SQLite and acknowledged immediately (200 OK with job ID)
- Worker claims job — a background worker picks up the job from the queue
- ffprobe detection — the handler runs ffprobe on the file path from the webhook payload to detect audio languages
- Link decision — based on detected languages and the instance config:
- Multi-audio (e.g., FR + EN): symlinks created in both FR and EN libraries
- Single language: symlink created in the matching library only
- Primary single-language: the alternate instance is told to fetch its own copy (cross-instance propagation)
- Upgrade handling — if the webhook indicates an upgrade, old links are removed before creating new ones
- Delete handling — when content is deleted, corresponding links are removed from all libraries
Job queue
Every webhook becomes a row in the SQLite jobs table. This gives:
- Crash recovery — if the container restarts mid-processing, pending jobs resume
- Retry with backoff — transient failures (network, filesystem) are retried automatically
- Audit trail — every event is recorded with its payload, status, and any errors
- Reprocessing —
POST /api/v1/jobs/reprocessreplays all historical events
Cross-instance propagation
When a primary instance (e.g., Radarr-FR) imports a single-language file:
- multilinguarr creates a symlink in the primary library (FR)
- It calls the alternate instance's API (Radarr-EN) to add the same movie
- Radarr-EN searches for and downloads its own copy
- When Radarr-EN's download completes, its webhook fires and multilinguarr creates the EN library link
This ensures each instance has its own file — no shared storage between instances.
Regeneration
POST /api/v1/admin/regenerate walks all storage directories, runs ffprobe on every file, and rebuilds the entire link tree. Use this:
- After initial setup (to process existing media)
- After a config change (new instance or language added)
- For recovery (if links were accidentally deleted)
Add ?dry_run=true to preview what would be done without touching the filesystem.