Forward a Telegram link → it lands in Plex, renamed and organized.
Quick Start · Features · Screenshots · How It Works
PlexBot is an async Telegram bot that downloads media from Telegram groups using tdl, automatically detects titles and metadata, matches them against TMDb, renames files for Plex compatibility (ASCII-only, SxxExx format), and places them into the correct library folders — all with zero manual renaming.
| 🔍 Smart Detection | Extracts title, season, episode, and year from messy scene-release filenames |
| 🎬 TMDb Integration | Auto-searches TMDb and shows top results with posters |
| 📁 Plex Naming | S01E02 - Title.mkv for series, Title (Year).mkv for movies — ASCII-safe |
| ⚡ Auto-Libraries | Skips selection if the show already has a folder on disk |
| 📦 Batch Downloads | Forward multiple files, confirm once, all queue up |
| 🔄 Quick Re-adds | Recent destinations cached for one-tap re-downloads |
| 🗜️ Archive Support | Automatic RAR/ZIP/7z extraction after download |
| 📊 Queue Management | FIFO single-worker with progress bars and per-title cancel |
| 👥 Multi-user | Any member of an allowed group can send links |
| 💾 No Database | In-memory session cache — lightweight and fast |
git clone https://github.com/Ukly0/telegram-to-plex.git plexbot
cd plexbot
./setup.shThe script will ask for your credentials, generate config files, pull the Docker image, and start the bot.
After setup, authenticate tdl:
docker exec -it -u plexbot -e TDL_HOME=/data/tdl $(docker compose ps -q plexbot) tdl login -T qrClick to expand
Message @BotFather on Telegram → /newbot → copy the token.
Register at https://www.themoviedb.org/settings/api → request an API key (free).
git clone https://github.com/Ukly0/telegram-to-plex.git plexbot
cd plexbot
cp config/.env.example config/.env
cp config/libraries.yaml.example config/libraries.yamlEdit config/.env with your credentials:
TELEGRAM_BOT_TOKEN=123456:ABC-DEF
TMDB_API_KEY=your_tmdb_bearer_token
ADMIN_USER_IDS=123456789
ALLOWED_CHAT_IDS=-1001234567890Edit docker-compose.yml to mount your media paths (or create docker-compose.override.yml):
services:
plexbot:
volumes:
- /your/host/tv:/media/tv
- /your/host/movies:/media/movies
- /your/host/anime:/media/animedocker compose up -ddocker exec -it -u plexbot -e TDL_HOME=/data/tdl $(docker compose ps -q plexbot) tdl login -T qrIn @BotFather: /mybots → your bot → Bot Settings → Group Privacy → Turn off
Then remove and re-add the bot to your group. Without this, the bot cannot see forwarded messages/files.
| Value | How to get it |
|---|---|
| Admin User ID | Message @userinfobot on Telegram |
| Group Chat ID | Add @RawDataBot to your group, it will reply with the chat ID, then remove it |
The prebuilt image is published at ghcr.io/ukly0/telegram-to-plex:latest and is pulled automatically by docker compose up.
To build locally instead:
docker compose build
docker compose up -d| Path | Purpose |
|---|---|
./config:/app/config:ro |
Bot configuration (.env, libraries.yaml) |
plexbot-data:/data |
tdl session (authentication persists here) |
/your/media/tv:/media/tv |
Your Plex TV library |
/your/media/movies:/media/movies |
Your Plex Movies library |
/your/media/anime:/media/anime |
Your Plex Anime library |
If downloads fail with not authorized, re-login:
docker exec -it -u plexbot -e TDL_HOME=/data/tdl $(docker compose ps -q plexbot) tdl login -T qr| Variable | Required | Description |
|---|---|---|
TELEGRAM_BOT_TOKEN |
Yes | Telegram bot token from @BotFather |
TMDB_API_KEY |
Yes | TMDb API v3 Bearer token |
ALLOWED_CHAT_IDS |
Yes | Comma/space separated Telegram chat IDs where the bot may run |
ADMIN_USER_IDS |
Yes | Comma/space separated Telegram user IDs allowed to use admin commands |
ADMIN_CHAT_ID |
No | Legacy alias for ADMIN_USER_IDS |
TDL_HOME |
No | Path to tdl session directory (default: ~/.tdl) |
| Type | Behavior | Naming | Folder Structure |
|---|---|---|---|
series |
Asks for season number | S01E02 - Title.mkv |
Show (Year)/Season 01/ |
movie |
Auto-queues immediately | Title (Year).mkv |
Title (Year)/ |
In config/libraries.yaml:
download:
tdl_template: 'tdl dl -u {url} -d "{dir}" -t 16 -l 9 --reconnect-timeout 0 --template "{{ .FileName }}"'
# tdl_home: /data/tdl # optional: separate session directory{url}and{dir}are replaced at runtime--template "{{ .FileName }}"preserves original filenames (avoids Go template conflicts)-t 16= 16 threads,-l 9= log level 9 (progress)
┌─────────────────────────────────────────────────────────┐
│ User sends link/file │
└────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────┐
│ Filename parsing │
│ "Show.S01E02. │
│ 1080p.WEB-DL.mkv" │
│ ↓ │
│ Title: "Show" │
│ Season: 1 │
│ Episode: 2 │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ TMDb auto-search │
│ → Top 3 results │
│ → Poster images │
└──────────┬──────────┘
│
┌──────────┴──────────┐
│ │
▼ ▼
┌─────────┐ ┌──────────┐
│ Series │ │ Movie │
│ │ │ │
│ Pick │ │ Auto- │
│ season │ │ queue │
└────┬─────┘ └────┬─────┘
│ │
▼ ▼
┌──────────────────────────────┐
│ Download via tdl │
│ (FIFO queue, progress bar) │
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ Post-processing │
│ 1. Extract archives (RAR/ZIP)│
│ 2. Rename for Plex │
│ 3. Set permissions (1000:1000)│
└──────────────────────────────┘
PlexBot detects metadata from messy filenames:
| Input | Title | Season | Year |
|---|---|---|---|
Breaking.Bad.S01E02.1080p.WEB-DL.x264.mkv |
Breaking Bad | 1 | — |
Euphoria.(2019).S03E05.1080p.WEB-DL.mkv |
Euphoria | 3 | 2019 |
Oppenheimer.(2023).1080p.WEB-DL.mkv |
Oppenheimer | — | 2023 |
Te van a matar (2026) by kowalski&xusman |
Te van a matar | — | 2026 |
Greenland 2 (2026) UHD BluRay REMUX 2160p |
Greenland 2 | — | 2026 |
Extracted season is pre-filled in the season picker. Year is used in folder names. All SxxExx patterns, resolution tags, codec names, language codes, and release group suffixes are stripped before TMDb search.
| Command | Description |
|---|---|
/start |
Show main menu |
/menu |
Return to dashboard |
/search |
Manual TMDb search |
/queue |
View running/pending downloads |
/cancel |
Cancel current flow + running download |
/cancel_all |
Cancel everything for this chat |
/clean_tmp |
Remove temp download folders (admin only) |
- Works only in chats listed in
ALLOWED_CHAT_IDS, plus private chats with users inADMIN_USER_IDS - In allowed groups, any member can send links — state is scoped per chat
- If the bot receives an update from an unauthorized group, it logs the chat ID and leaves that group
- Groups must be public (or have a public invite link) for
tdlto resolve forwarded message download links
app/
├── bot.py # Entry point — handler registration
├── config.py # Libraries YAML + .env loader
├── state.py # Conversation state constants + reset
├── handlers/
│ ├── ingest.py # Link/file intake — auto metadata, batch handling
│ ├── search.py # TMDb search, season/library selection
│ ├── menu.py # /start, /menu, dashboard, queue view
│ └── download.py # Download queue, tdl subprocess, post-process
└── services/
├── tmdb.py # TMDb API client
├── downloader.py # tdl subprocess wrapper — progress, retries
├── extractor.py # RAR/ZIP/7z detection and extraction
└── namer.py # Plex-safe naming — ASCII, SxxExx, collision handling
config/
└── libraries.yaml # Library definitions (user-editable)
- No persistence — in-memory state resets on restart (download queue, recent destinations, conversation state)
- Single download worker — downloads are sequential (one
tdlat a time to avoid TDLib session conflicts) - Public groups only —
tdlcannot resolve download links from private Telegram groups
Contributions are welcome! If you have ideas for improvements or find bugs, feel free to:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
If you find this project useful, consider giving it a ⭐️ on GitHub!
For issues or questions, please open an issue.
- tdl — Telegram Downloader, the engine behind all downloads. Fast, reliable, and open source.
- python-telegram-bot — Python wrapper for the Telegram Bot API.
- TMDb — The Movie Database, for metadata and poster images.





