Folks kept asking me
How do I get my perfectly legal movie/file/show lined up and ready to download without juggling between unreachable websites, ads and all like I’m auditioning for Cirque du Soleil?
So I got tired of answering and wrote this toolkit instead.
Pour a drink and let torrent_finder do the heavy lifting.
- Talks to your friendly neighborhood Torznab/Jackett endpoint and hunts down magnets that actually match what you asked for.
- Ranks the hits by seeders (because buffering is for people who make salad at barbecues).
- Tosses the winner straight into Transmission.
- Wraps it all in a tidy class-based architecture with a configuration file so you can stop memorizing stuff.
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtIf you plan on using the RPC interface and Python throws a fit about transmission_rpc, that means you skipped the third line. Don’t be that person.
Copy config.example.json to config.json and fill in your grown-up details.
yes, the defaults are just suggestions:
{
"torznab": {
"url": "http://localhost:9117/jackett/torznab/all",
"apikey": "CHANGE_ME",
"categories": "2000"
},
"transmission": {
"download_dir": "/path/to/save",
"start": false,
"use_rpc": true,
"host": "localhost",
"port": 9091,
"username": "transmission",
"password": "transmission"
},
"logging": {
"level": "INFO"
}
}Highlights:
torznab.urlandtorznab.apikeyare the secret handshake to your indexers. No handshake, no magnets.torznab.categorieskeeps the noise down. Comma-separated, like a grocery list, minus the kale—and now you can pass--category movies|tv|comics|software|software-mac|software-win|zip|allfor the common presets without remembering IDs.transmission.startdecides if Transmission hits the gas or waits politely in park.logging.levelunderstands "DEBUG", "INFO", "WARNING", and "ERROR". No, "LOUD" is not an option.- Add a
telegramsection when you want the chat bot to pick up credentials:
"telegram": {
"bot_token": "123456:ABC",
"chat_id": "123456789"
}chat_id is optional but keeps random chats from hijacking your downloads.
If you do not already have Jackett and FlareSolverr on your machine, the project ships with a helper:
python scripts/setup_indexing_stack.pyWhat it does:
- Detects running instances before doing anything destructive.
- Writes a
docker-compose.yml(linuxserver/jackett + ghcr.io/flaresolverr) under~/.local/share/torrent_finder/stackand brings it up via Docker. - Links Jackett to FlareSolverr and scaffolds the compose stack so you can begin adding trackers (indexers are not added automatically).
- Prints the local Jackett URL so you can finish setup manually—log in, add the indexers you care about, and copy the Torznab API key back into
config.json. (Jackett does not expose that key via automation.) - Updates
config.jsonwhentorznab.url/apikeyare still on the placeholder values once you paste the key.
Use --help for overrides (custom ports, tracker list, skipping Docker, or forcing a config update). Docker has to be installed if you want the automatic install; otherwise run with --no-docker and the script will only perform the API wiring.
After bootstrapping: open the printed Jackett URL (usually
http://127.0.0.1:9117), walk through the UI to add one or more indexers (e.g. Jackett →Add Indexer), then copy the API key from the Jackett dashboard intoconfig.json. This step is manual—Jackett currently offers no supported way to script API-key retrieval or indexer imports without user interaction.
python main.py "I wil not write the tile of the movie here"Need to override something on a whim?
python main.py "Nature Documentary" \
--config config.json \
--download-dir "/external/drive" \
--start \
--username transmission \
--password secret \
--category moviesPrefer raw Torznab IDs? Keep --categories "2000,5000" around for custom combos—the presets just save you from memorizing them.
| Preset | Under the hood | Sample CLI usage | Telegram equivalent |
|---|---|---|---|
movies |
2000 |
--category movies |
search movies dune |
tv |
5000 |
--category tv |
search tv the bear |
comics |
7030 |
--category comics |
search comics saga |
software |
4000 |
--category software |
search software blender |
software-mac |
4050 |
--category software-mac |
search software mac final cut |
software-win |
4010,4020 |
--category software-win |
search software win office |
zip |
8000 |
--category zip |
search zip files pack |
all |
no filter | --category all |
search all dune |
The presets piggyback on top of torznab.categories, so they work without touching config.json. Mix and match with the usual overrides to script one-off runs.
Want to drive searches from your phone? Spin up the Telegram bot:
pip install -r requirements.txt # once
python telegram_bot.py --token "<bot api token>" --config config.jsonFlow:
- Run
/startto open the inline menu, then tap Search or a category button (you can still typesearch <title>). - Add an optional category word—
search movies dune,search tv s04,search comics saga,search software mac final cut,search zip files pack, orsearch all dune—to switch Torznab filters without editing the config. - It responds with ranked cards (title, seed/peer counts, size, source) plus Next/Prev paging and a More like button.
- Reply with the list number or tap the inline button to pick a result; the bot will then ask where to save it and show a couple of download directory buttons.
- Update the directory presets in
telegram_bot.py(_download_dir_options) to match your Transmission setup before relying on the buttons; you can add more paths or point them at your existing watch/download folders. - Send
status(or tap Status) any time to see every torrent plus a quick explanation of each state (downloading, seeding, stopped, etc.). Active/All filters and a refresh button keep the status message in place. The status block includes the Transmission ID for removals. - The bot pings you once a Telegram-triggered download finishes so you can hit play quicker.
The token can also come from the telegram.bot_token section in config.json (or TELEGRAM_TOKEN). Add
telegram.chat_id if you want to lock the bot to a single chat/channel. Use --max-results to tweak
how many options are shown. /start opens the inline menu with Search/Status/Help plus category shortcuts.
Quick commands
search <query>– fetches results; prefix withmovies,tv,comics,software,software mac,software win,zip, orallto reuse the presets listed above./start_magnet <magnet_url>– send a magnet link straight to Transmission.<number>– selects one of the previous results (inline buttons do the same).status– lists every torrent and annotates Transmission’s reported state (also available via the Status button)./remove <id or name>– stop and remove a torrent (use the numeric ID fromstatusfor precision).help//help– shows the condensed cheat-sheet.
Heads up: background status polling uses Python Telegram Bot's JobQueue. Install the optional extra via
pip install "python-telegram-bot[job-queue]"to enable the completion pings. Missing the extra? The bot will fall back to a lightweight asyncio poller so you still get completion notices.
- DM @BotFather, run
/newbot, follow the prompts, and copy the API token it prints (looks like1234567890:ABC...). - Drop that token into
config.jsonundertelegram.bot_token(or exportTELEGRAM_TOKEN, or pass--tokenwhen running the bot). - (Optional) Lock the bot to a single chat: start a conversation with your bot (or add it to the group), send
/start, then runcurl "https://api.telegram.org/bot<token>/getUpdates"; copy thechat.idshown in the JSON. Alternatively, DM @userinfobot and reuse theIdit reports. - Store that numeric ID in
telegram.chat_id.
Skip the chat ID to let the bot respond to any chat during initial testing.
Crank telemetry up with --telemetry-level DEBUG (or pass --torznab-debug) when you need the raw Jackett
response previews logged to stdout—handy when a feed returns zero results and you want to know why.
Install the dev tooling once and let Black keep indentation sane:
pip install -r requirements-dev.txt
black .Use black --check . locally or in CI to make sure everything stays formatted.
The documentation site now uses MkDocs.
pip install -r requirements.txt # already includes mkdocs
mkdocs serve # live-reload server at http://127.0.0.1:8000/
mkdocs build # produce the static site in ./sitetorrent_finder/config.pyloads and validates JSON like a responsible adult.torrent_finder/torznab.pyhandles the Torznab tango, filters titles, and keeps notes on seeders and leechers.torrent_finder/finder.pypicks the winner.torrent_finder/transmission.pytalks to Transmission.main.pyglues it together, handles overrides, and logs the play-by-play.
- "No matching items": Either your indexers are empty or you searched for something that doesn’t exist. Maybe don’t.
- "transmission-remote not found": Install Transmission or switch to RPC. Wishing real hard won’t make it appear.
- "Invalid JSON configuration": You missed a comma. Computers are picky like that.
Use it for your own stuff, or with permission. You get caught pirating, that’s on you. I’ll be over here, sipping whiskey, guinness and enjoying the show.