pyTorrent
Single-page web UI for rTorrent inspired by the ruTorrent workflow.
Features
- Flask + Flask-SocketIO.
- SQLite storage for preferences, SCGI profiles, Bootstrap theme and UI font.
- Multiple rTorrent profiles per user.
- Profiles can be added and edited from the UI; the remote profile flag hides local CPU/RAM usage to avoid confusing it with remote rTorrent host resources.
- Active rTorrent profile switching from the UI.
- Live torrent list over WebSocket.
- Application-side cache with patch updates instead of full table reloads.
- User operations executed through ThreadPoolExecutor.
moveandremoveactions are executed per profile in request order, so later deletes wait for earlier moves.- Job log shows a short date/time in the table and the full timestamp in the tooltip.
- Bulk start, pause, stop, resume, recheck, remove and move.
- Move supports
move_data=true; data is physically moved on the rTorrent side in the background and status is polled from a marker file, so longmvoperations do not hit the SCGI timeout. - Multi-magnet add modal.
- Bottom status bar with CPU, RAM, rTorrent version, speeds, limits, total DL/UP and port-check status when enabled.
- Torrent context menu.
- Keyboard shortcuts.
- Details tabs: General, Files, Peers, Trackers and Log.
- Smart Queue shows the last 10 operations by default and can expand history to 100 rows.
- Peer GeoIP with MaxMind GeoLite2-City.mmdb and IP cache.
- Static cache busting with MD5 and cache headers.
- Appearance preferences: default Bootstrap or Bootswatch themes Flatly, Litera, Lumen, Minty, Sketchy, Solar, Spacelab, United and Zephyr.
- Font preferences: default theme font, Adwaita Mono and additional matching fonts.
Run locally
./install.sh
. venv/bin/activate
python app.py
Default URL: http://127.0.0.1:8090.
Production run
Preferred mode without development Werkzeug:
. venv/bin/activate
gunicorn --worker-class gthread --workers 1 --threads 32 --bind 0.0.0.0:8090 --access-logfile - --error-logfile - wsgi:app
Note: the app keeps async_mode="threading", so WebSocket, start_background_task, operation queues and the poller run in the same model as before.
Alternatives reviewed but not enabled by default:
- Gunicorn with
eventlet: works with Flask-SocketIO, but requires green threads and monkey patching, which increases regression risk for file and SCGI operations. - Gunicorn with
gevent: a valid production option, but it needs extra dependencies and compatibility testing. - Multiple Gunicorn workers: requires Redis, RabbitMQ or Kafka as the Socket.IO message queue, so it is not a drop-in replacement.
Reverse proxy
When pyTorrent is served behind a reverse proxy, enable proxy header handling only when the proxy is trusted:
PYTORRENT_PROXY_FIX_ENABLE=true
PYTORRENT_SESSION_COOKIE_SECURE=true
The proxy should forward at least:
X-Forwarded-For
X-Forwarded-Proto
X-Forwarded-Host
X-Forwarded-Port
This keeps login redirects, session cookies and same-origin API checks correct when HTTPS is terminated by the proxy. If pyTorrent is mounted under a sub-path, also forward X-Forwarded-Prefix.
SCGI profile
Example:
scgi://127.0.0.1:5000/RPC2
On the rTorrent side:
network.scgi.open_port = 127.0.0.1:5000
GeoIP
The installer downloads GeoLite2-City once to:
data/GeoLite2-City.mmdb
Manual download:
./scripts/download_geoip.sh
The script uses https://git.io/GeoLite2-City.mmdb as the primary source and https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-City.mmdb as fallback. The data directory is set to 755, and the database file is set to 644.
API docs
OpenAPI documentation is available at /docs. /api/profiles supports max_parallel_jobs with default value 5 and is_remote; PUT /api/profiles/{profile_id} edits an existing profile. /api/preferences supports fields including theme, bootstrap_theme, font_family, table_columns_json, peers_refresh_seconds and port_check_enabled. /api/port-check returns port status with checked_at; for remote profiles the public IP is read through rTorrent with fallbacks when supported. /api/system/status returns usage_available=false for remote profiles and does not read local CPU/RAM.
/api/openapi.json includes reusable schemas for main API responses, including TorrentListResponse, TorrentSummary, TorrentFilterSummary, CleanupSummary and AppStatus. GET /api/torrents documents the summary field used by sidebar filters.
Admin CLI
Reset an existing user's password:
. venv/bin/activate
python -m pytorrent.cli reset-password admin new_password
Without the password argument, the CLI asks for it interactively:
python -m pytorrent.cli reset-password admin
The command uses the same database as the app and respects PYTORRENT_DB_PATH from .env. The reset changes only the password hash and leaves role and permissions unchanged.