# 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. - `move` and `remove` actions 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 long `mv` operations 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 ```bash ./install.sh . venv/bin/activate python app.py ``` Default URL: `http://127.0.0.1:8090`. ## Production run Preferred mode without development Werkzeug: ```bash . 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: ```env PYTORRENT_PROXY_FIX_ENABLE=true PYTORRENT_SESSION_COOKIE_SECURE=true ``` The proxy should forward at least: ```txt 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: ```txt scgi://127.0.0.1:5000/RPC2 ``` On the rTorrent side: ```txt network.scgi.open_port = 127.0.0.1:5000 ``` ## GeoIP The installer downloads GeoLite2-City once to: ```txt data/GeoLite2-City.mmdb ``` Manual download: ```bash ./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: ```bash . venv/bin/activate python -m pytorrent.cli reset-password admin new_password ``` Without the password argument, the CLI asks for it interactively: ```bash 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.